diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 86fdba8670f..d8ff9671259 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -92,12 +92,26 @@ jobs: id: gtest uses: ./.github/actions/get-gtest + - name: 'Check toolchain installed' + id: toolchain-check + run: | + set +e + '/c/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/vc/auxiliary/build/vcvars64.bat' -vcvars_ver=${{ inputs.msvc-toolset-version }} + if [ $? -eq 0 ]; then + echo "Toolchain is already installed" + echo "toolchain-installed=true" >> $GITHUB_OUTPUT + else + echo "Toolchain is not yet installed" + echo "toolchain-installed=false" >> $GITHUB_OUTPUT + fi + - name: 'Install toolchain and dependencies' run: | # Run Visual Studio Installer '/c/Program Files (x86)/Microsoft Visual Studio/Installer/vs_installer.exe' \ - modify --quiet --installPath 'C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise' \ + modify --quiet --installPath 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise' \ --add Microsoft.VisualStudio.Component.VC.${{ inputs.msvc-toolset-version }}.${{ inputs.msvc-toolset-architecture }} + if: steps.toolchain-check.outputs.toolchain-installed != 'true' - name: 'Configure' run: > diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 88b68eadb9c..525f6d27f69 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -223,7 +223,7 @@ jobs: uses: ./.github/workflows/build-windows.yml with: platform: windows-x64 - msvc-toolset-version: '14.25' + msvc-toolset-version: '14.29' msvc-toolset-architecture: 'x86.x64' if: needs.select.outputs.windows-x64 == 'true' diff --git a/.gitignore b/.gitignore index 6787b232535..d0736707b80 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ NashornProfile.txt /src/utils/LogCompilation/target/ /.project/ /.settings/ +/compile_commands.json +/.cache diff --git a/.jcheck/conf b/.jcheck/conf index 495417d1972..b8301502d72 100644 --- a/.jcheck/conf +++ b/.jcheck/conf @@ -1,7 +1,7 @@ [general] project=jdk-updates jbs=JDK -version=17.0.7 +version=17.0.8 [checks] error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists diff --git a/bin/jib.sh b/bin/jib.sh index aab198990cc..9f34cf9ab7b 100644 --- a/bin/jib.sh +++ b/bin/jib.sh @@ -128,6 +128,15 @@ install_jib() { exit 1 fi fi + # Want to check the filetype using file, to see if we got served a HTML error page. + # This is sensitive to the filename containing a specific string, but good enough. + file "${installed_jib_script}.gz" | grep "gzip compressed data" > /dev/null + if [ $? -ne 0 ]; then + echo "Warning: ${installed_jib_script}.gz is not a gzip file." + echo "If you are behind a proxy you may need to configure exceptions using no_proxy." + echo "The download URL was: ${jib_url}" + exit 1 + fi echo "Extracting JIB bootstrap script" rm -f "${installed_jib_script}" gunzip "${installed_jib_script}.gz" diff --git a/doc/building.html b/doc/building.html index 49035c0d625..f92b3680009 100644 --- a/doc/building.html +++ b/doc/building.html @@ -273,7 +273,7 @@

Native Compiler (Toolchain) Requ Linux -gcc 10.2.0 +gcc 11.2.0 macOS @@ -288,7 +288,7 @@

Native Compiler (Toolchain) Requ

All compilers are expected to be able to compile to the C99 language standard, as some C99 features are used in the source code. Microsoft Visual Studio doesn't fully support C99 so in practice shared code is limited to using C99 features that it does support.

gcc

The minimum accepted version of gcc is 5.0. Older versions will generate a warning by configure and are unlikely to work.

-

The JDK is currently known to be able to compile with at least version 10.2 of gcc.

+

The JDK is currently known to be able to compile with at least version 11.2 of gcc.

In general, any version between these two should be usable.

clang

The minimum accepted version of clang is 3.5. Older versions will not be accepted by configure.

diff --git a/doc/building.md b/doc/building.md index 30ec48b78e2..a11ed884610 100644 --- a/doc/building.md +++ b/doc/building.md @@ -321,7 +321,7 @@ issues. | Operating system | Toolchain version | | ------------------ | ------------------------------------------ | -| Linux | gcc 10.2.0 | +| Linux | gcc 11.2.0 | | macOS | Apple Xcode 10.1 (using clang 10.0.0) | | Windows | Microsoft Visual Studio 2022 update 17.1.0 | @@ -335,7 +335,7 @@ features that it does support. The minimum accepted version of gcc is 5.0. Older versions will generate a warning by `configure` and are unlikely to work. -The JDK is currently known to be able to compile with at least version 10.2 of +The JDK is currently known to be able to compile with at least version 11.2 of gcc. In general, any version between these two should be usable. diff --git a/doc/testing.html b/doc/testing.html index effc6f0c446..e9ff7863497 100644 --- a/doc/testing.html +++ b/doc/testing.html @@ -242,14 +242,39 @@

PKCS11 Tests

JTREG="JAVA_OPTIONS=-Dtest.nss.lib.paths=/path/to/your/latest/NSS-libs"

For more notes about the PKCS11 tests, please refer to test/jdk/sun/security/pkcs11/README.

Client UI Tests

+

System key shortcuts

Some Client UI tests use key sequences which may be reserved by the operating system. Usually that causes the test failure. So it is highly recommended to disable system key shortcuts prior testing. The steps to access and disable system key shortcuts for various platforms are provided below.

-

MacOS

+
MacOS

Choose Apple menu; System Preferences, click Keyboard, then click Shortcuts; select or deselect desired shortcut.

For example, test/jdk/javax/swing/TooltipManager/JMenuItemToolTipKeyBindingsTest/JMenuItemToolTipKeyBindingsTest.java fails on MacOS because it uses CTRL + F1 key sequence to show or hide tooltip message but the key combination is reserved by the operating system. To run the test correctly the default global key shortcut should be disabled using the steps described above, and then deselect "Turn keyboard access on or off" option which is responsible for CTRL + F1 combination.

-

Linux

+
Linux

Open the Activities overview and start typing Settings; Choose Settings, click Devices, then click Keyboard; set or override desired shortcut.

-

Windows

+
Windows

Type gpedit in the Search and then click Edit group policy; navigate to User Configuration -> Administrative Templates -> Windows Components -> File Explorer; in the right-side pane look for "Turn off Windows key hotkeys" and double click on it; enable or disable hotkeys.

Note: restart is required to make the settings take effect.

+

Robot API

+

Most automated Client UI tests use Robot API to control +the UI. Usually, the default operating system settings need to be +adjusted for Robot to work correctly. The detailed steps how to access +and update these settings for different platforms are provided +below.

+
macOS
+

Robot is not permitted to control your Mac by default +since macOS 10.15. To allow it, choose Apple menu -> System Settings, +click Privacy & Security; then click Accessibility and ensure the +following apps are allowed to control your computer: Java and +Terminal. If the tests are run from an IDE, the IDE should be +granted this permission too.

+
Windows
+

On Windows if Cygwin terminal is used to run the tests, there is a +delay in focus transfer. Usually it causes automated UI test failure. To +disable the delay, type regedit in the Search and then +select Registry Editor; navigate to the following key: +HKEY_CURRENT_USER\Control Panel\Desktop; make sure the +ForegroundLockTimeout value is set to 0.

+

Additional information about Client UI tests configuration for +various operating systems can be obtained at Automated +client GUI testing system set up requirements

diff --git a/doc/testing.md b/doc/testing.md index 241bc08b40b..7f11fb2b511 100644 --- a/doc/testing.md +++ b/doc/testing.md @@ -546,12 +546,14 @@ test/jdk/sun/security/pkcs11/README. ### Client UI Tests +#### System key shortcuts + Some Client UI tests use key sequences which may be reserved by the operating system. Usually that causes the test failure. So it is highly recommended to disable system key shortcuts prior testing. The steps to access and disable system key shortcuts for various platforms are provided below. -#### MacOS +##### macOS Choose Apple menu; System Preferences, click Keyboard, then click Shortcuts; select or deselect desired shortcut. @@ -564,12 +566,12 @@ test correctly the default global key shortcut should be disabled using the steps described above, and then deselect "Turn keyboard access on or off" option which is responsible for `CTRL + F1` combination. -#### Linux +##### Linux Open the Activities overview and start typing Settings; Choose Settings, click Devices, then click Keyboard; set or override desired shortcut. -#### Windows +##### Windows Type `gpedit` in the Search and then click Edit group policy; navigate to User Configuration -> Administrative Templates -> Windows Components -> File @@ -578,6 +580,33 @@ double click on it; enable or disable hotkeys. Note: restart is required to make the settings take effect. +#### Robot API + +Most automated Client UI tests use `Robot` API to control the UI. Usually, +the default operating system settings need to be adjusted for Robot +to work correctly. The detailed steps how to access and update these settings +for different platforms are provided below. + +##### macOS + +`Robot` is not permitted to control your Mac by default since +macOS 10.15. To allow it, choose Apple menu -> System Settings, click +Privacy & Security; then click Accessibility and ensure the following apps are +allowed to control your computer: *Java* and *Terminal*. If the tests are run +from an IDE, the IDE should be granted this permission too. + +##### Windows + +On Windows if Cygwin terminal is used to run the tests, there is a delay in +focus transfer. Usually it causes automated UI test failure. To disable the +delay, type `regedit` in the Search and then select Registry Editor; navigate +to the following key: `HKEY_CURRENT_USER\Control Panel\Desktop`; make sure +the `ForegroundLockTimeout` value is set to 0. + +Additional information about Client UI tests configuration for various operating +systems can be obtained at [Automated client GUI testing system set up +requirements](https://wiki.openjdk.org/display/ClientLibs/Automated+client+GUI+testing+system+set+up+requirements) + --- # Override some definitions in the global css file that are not optimal for # this document. diff --git a/make/ReleaseFile.gmk b/make/ReleaseFile.gmk index 0424e2fb623..5e8d123fac9 100644 --- a/make/ReleaseFile.gmk +++ b/make/ReleaseFile.gmk @@ -51,6 +51,7 @@ define create-info-file $(if $(VENDOR_VERSION_STRING), \ $(call info-file-item, "IMPLEMENTOR_VERSION", "$(VENDOR_VERSION_STRING)")) $(call info-file-item, "JAVA_VERSION_DATE", "$(VERSION_DATE)") + $(call info-file-item, "JAVA_RUNTIME_VERSION", "$(VERSION_STRING)") $(call info-file-item, "OS_NAME", "$(RELEASE_FILE_OS_NAME)") $(call info-file-item, "OS_ARCH", "$(RELEASE_FILE_OS_ARCH)") $(call info-file-item, "LIBC", "$(RELEASE_FILE_LIBC)") diff --git a/make/autoconf/basic.m4 b/make/autoconf/basic.m4 index c16aba0881e..8461815d771 100644 --- a/make/autoconf/basic.m4 +++ b/make/autoconf/basic.m4 @@ -55,6 +55,7 @@ AC_DEFUN([BASIC_CHECK_LEFTOVER_OVERRIDDEN], ############################################################################### # Setup basic configuration paths, and platform-specific stuff related to PATHs. +# Make sure to only use tools set up in BASIC_SETUP_FUNDAMENTAL_TOOLS. AC_DEFUN_ONCE([BASIC_SETUP_PATHS], [ # Save the current directory this script was started from diff --git a/make/autoconf/basic_tools.m4 b/make/autoconf/basic_tools.m4 index ac24bbcb831..8c518d05d41 100644 --- a/make/autoconf/basic_tools.m4 +++ b/make/autoconf/basic_tools.m4 @@ -24,8 +24,8 @@ # ############################################################################### -# Setup the most fundamental tools that relies on not much else to set up, -# but is used by much of the early bootstrap code. +# Setup the most fundamental tools, used for setting up build platform and +# path handling. AC_DEFUN_ONCE([BASIC_SETUP_FUNDAMENTAL_TOOLS], [ # Bootstrapping: These tools are needed by UTIL_LOOKUP_PROGS @@ -37,7 +37,28 @@ AC_DEFUN_ONCE([BASIC_SETUP_FUNDAMENTAL_TOOLS], UTIL_CHECK_NONEMPTY(FILE) AC_PATH_PROGS(LDD, ldd) - # First are all the fundamental required tools. + # Required tools + UTIL_REQUIRE_PROGS(ECHO, echo) + UTIL_REQUIRE_PROGS(TR, tr) + UTIL_REQUIRE_PROGS(UNAME, uname) + UTIL_REQUIRE_PROGS(WC, wc) + + # Required tools with some special treatment + UTIL_REQUIRE_SPECIAL(GREP, [AC_PROG_GREP]) + UTIL_REQUIRE_SPECIAL(EGREP, [AC_PROG_EGREP]) + UTIL_REQUIRE_SPECIAL(SED, [AC_PROG_SED]) + + # Tools only needed on some platforms + UTIL_LOOKUP_PROGS(PATHTOOL, cygpath wslpath) + UTIL_LOOKUP_PROGS(CMD, cmd.exe, $PATH:/cygdrive/c/windows/system32:/mnt/c/windows/system32:/c/windows/system32) +]) + +############################################################################### +# Setup further tools that should be resolved early but after setting up +# build platform and path handling. +AC_DEFUN_ONCE([BASIC_SETUP_TOOLS], +[ + # Required tools UTIL_REQUIRE_PROGS(BASH, bash) UTIL_REQUIRE_PROGS(CAT, cat) UTIL_REQUIRE_PROGS(CHMOD, chmod) @@ -45,7 +66,6 @@ AC_DEFUN_ONCE([BASIC_SETUP_FUNDAMENTAL_TOOLS], UTIL_REQUIRE_PROGS(CUT, cut) UTIL_REQUIRE_PROGS(DATE, date) UTIL_REQUIRE_PROGS(DIFF, gdiff diff) - UTIL_REQUIRE_PROGS(ECHO, echo) UTIL_REQUIRE_PROGS(EXPR, expr) UTIL_REQUIRE_PROGS(FIND, find) UTIL_REQUIRE_PROGS(GUNZIP, gunzip) @@ -67,26 +87,18 @@ AC_DEFUN_ONCE([BASIC_SETUP_FUNDAMENTAL_TOOLS], UTIL_REQUIRE_PROGS(TAR, gtar tar) UTIL_REQUIRE_PROGS(TEE, tee) UTIL_REQUIRE_PROGS(TOUCH, touch) - UTIL_REQUIRE_PROGS(TR, tr) - UTIL_REQUIRE_PROGS(UNAME, uname) - UTIL_REQUIRE_PROGS(WC, wc) UTIL_REQUIRE_PROGS(XARGS, xargs) - # Then required tools that require some special treatment. - UTIL_REQUIRE_SPECIAL(GREP, [AC_PROG_GREP]) - UTIL_REQUIRE_SPECIAL(EGREP, [AC_PROG_EGREP]) + # Required tools with some special treatment UTIL_REQUIRE_SPECIAL(FGREP, [AC_PROG_FGREP]) - UTIL_REQUIRE_SPECIAL(SED, [AC_PROG_SED]) # Optional tools, we can do without them UTIL_LOOKUP_PROGS(DF, df) UTIL_LOOKUP_PROGS(NICE, nice) UTIL_LOOKUP_PROGS(READLINK, greadlink readlink) - # These are only needed on some platforms - UTIL_LOOKUP_PROGS(PATHTOOL, cygpath wslpath) + # Tools only needed on some platforms UTIL_LOOKUP_PROGS(LSB_RELEASE, lsb_release) - UTIL_LOOKUP_PROGS(CMD, cmd.exe, $PATH:/cygdrive/c/windows/system32:/mnt/c/windows/system32:/c/windows/system32) # For compare.sh only UTIL_LOOKUP_PROGS(CMP, cmp) diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac index c7046ae41ea..ebf43c363ea 100644 --- a/make/autoconf/configure.ac +++ b/make/autoconf/configure.ac @@ -86,6 +86,7 @@ PLATFORM_SETUP_OPENJDK_BUILD_AND_TARGET # Continue setting up basic stuff. Most remaining code require fundamental tools. BASIC_SETUP_PATHS +BASIC_SETUP_TOOLS # Check if it's a pure open build or if custom sources are to be used. JDKOPT_SETUP_OPEN_OR_CUSTOM diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4 index 96d68b587f1..4f86ed5a4bc 100644 --- a/make/autoconf/flags-cflags.m4 +++ b/make/autoconf/flags-cflags.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -188,6 +188,10 @@ AC_DEFUN([FLAGS_SETUP_WARNINGS], WARNINGS_ENABLE_ALL_CXXFLAGS="$WARNINGS_ENABLE_ALL_CFLAGS $WARNINGS_ENABLE_ADDITIONAL_CXX" DISABLED_WARNINGS="unused-parameter unused" + # gcc10/11 on ppc generate lots of abi warnings about layout of aggregates containing vectors + if test "x$OPENJDK_TARGET_CPU_ARCH" = "xppc"; then + DISABLED_WARNINGS="$DISABLED_WARNINGS psabi" + fi ;; clang) diff --git a/make/autoconf/jvm-features.m4 b/make/autoconf/jvm-features.m4 index 906a2857877..7cb66a95aaa 100644 --- a/make/autoconf/jvm-features.m4 +++ b/make/autoconf/jvm-features.m4 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -248,8 +248,11 @@ AC_DEFUN_ONCE([JVM_FEATURES_CHECK_CDS], AC_DEFUN_ONCE([JVM_FEATURES_CHECK_DTRACE], [ JVM_FEATURES_CHECK_AVAILABILITY(dtrace, [ - AC_MSG_CHECKING([for dtrace tool]) - if test "x$DTRACE" != "x" && test -x "$DTRACE"; then + AC_MSG_CHECKING([for dtrace tool and platform support]) + if test "x$OPENJDK_TARGET_CPU_ARCH" = "xppc"; then + AC_MSG_RESULT([no, $OPENJDK_TARGET_CPU_ARCH]) + AVAILABLE=false + elif test "x$DTRACE" != "x" && test -x "$DTRACE"; then AC_MSG_RESULT([$DTRACE]) else AC_MSG_RESULT([no]) diff --git a/make/autoconf/libraries.m4 b/make/autoconf/libraries.m4 index a65d91ee974..393d2819f1d 100644 --- a/make/autoconf/libraries.m4 +++ b/make/autoconf/libraries.m4 @@ -153,7 +153,7 @@ AC_DEFUN_ONCE([LIB_SETUP_LIBRARIES], if test "x$OPENJDK_TARGET_OS" = xwindows; then BASIC_JVM_LIBS="$BASIC_JVM_LIBS kernel32.lib user32.lib gdi32.lib winspool.lib \ - comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib \ + comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib powrprof.lib uuid.lib \ wsock32.lib winmm.lib version.lib psapi.lib" fi diff --git a/make/autoconf/platform.m4 b/make/autoconf/platform.m4 index 9e9e9454f0e..19842bf3467 100644 --- a/make/autoconf/platform.m4 +++ b/make/autoconf/platform.m4 @@ -632,6 +632,7 @@ AC_DEFUN([PLATFORM_SET_MODULE_TARGET_OS_VALUES], ]) #%%% Build and target systems %%% +# Make sure to only use tools set up in BASIC_SETUP_FUNDAMENTAL_TOOLS. AC_DEFUN_ONCE([PLATFORM_SETUP_OPENJDK_BUILD_AND_TARGET], [ # Figure out the build and target systems. # Note that in autoconf terminology, "build" is obvious, but "target" @@ -718,7 +719,7 @@ AC_DEFUN_ONCE([PLATFORM_SETUP_OPENJDK_TARGET_ENDIANNESS], [ ############################################################################### # - # Is the target little of big endian? + # Is the target little or big endian? # AC_C_BIGENDIAN([ENDIAN="big"],[ENDIAN="little"],[ENDIAN="unknown"],[ENDIAN="universal_endianness"]) diff --git a/make/common/NativeCompilation.gmk b/make/common/NativeCompilation.gmk index 0f315019d0e..215d90d17e9 100644 --- a/make/common/NativeCompilation.gmk +++ b/make/common/NativeCompilation.gmk @@ -343,10 +343,15 @@ define SetupCompileNativeFileBody endif endif + ifneq ($(DISABLE_WARNING_PREFIX), ) + $1_WARNINGS_FLAGS := $$(addprefix $(DISABLE_WARNING_PREFIX), \ + $$($$($1_BASE)_DISABLED_WARNINGS_$(TOOLCHAIN_TYPE)_$$($1_FILENAME))) + endif + $1_BASE_CFLAGS := $$($$($1_BASE)_CFLAGS) $$($$($1_BASE)_EXTRA_CFLAGS) \ - $$($$($1_BASE)_SYSROOT_CFLAGS) + $$($$($1_BASE)_SYSROOT_CFLAGS) $$($1_WARNINGS_FLAGS) $1_BASE_CXXFLAGS := $$($$($1_BASE)_CXXFLAGS) $$($$($1_BASE)_EXTRA_CXXFLAGS) \ - $$($$($1_BASE)_SYSROOT_CFLAGS) $$($1_EXTRA_CXXFLAGS) + $$($$($1_BASE)_SYSROOT_CFLAGS) $$($1_EXTRA_CXXFLAGS) $$($1_WARNINGS_FLAGS) $1_BASE_ASFLAGS := $$($$($1_BASE)_ASFLAGS) $$($$($1_BASE)_EXTRA_ASFLAGS) ifneq ($$(filter %.c, $$($1_FILENAME)), ) diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 72373a0837d..d5fdb982974 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -1048,10 +1048,10 @@ var getJibProfilesProfiles = function (input, common, data) { var getJibProfilesDependencies = function (input, common) { var devkit_platform_revisions = { - linux_x64: "gcc10.3.0-OL6.4+1.0", + linux_x64: "gcc11.2.0-OL6.4+1.0", macosx: "Xcode12.4+1.0", windows_x64: "VS2022-17.1.0+1.0", - linux_aarch64: "gcc10.3.0-OL7.6+1.0", + linux_aarch64: "gcc11.2.0-OL7.6+1.0", linux_arm: "gcc8.2.0-Fedora27+1.0", linux_ppc64le: "gcc8.2.0-Fedora27+1.0", linux_s390x: "gcc8.2.0-Fedora27+1.0" diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf index c28d27c6a72..488b0cac1ee 100644 --- a/make/conf/version-numbers.conf +++ b/make/conf/version-numbers.conf @@ -28,12 +28,12 @@ DEFAULT_VERSION_FEATURE=17 DEFAULT_VERSION_INTERIM=0 -DEFAULT_VERSION_UPDATE=7 +DEFAULT_VERSION_UPDATE=8 DEFAULT_VERSION_PATCH=0 DEFAULT_VERSION_EXTRA1=0 DEFAULT_VERSION_EXTRA2=0 DEFAULT_VERSION_EXTRA3=0 -DEFAULT_VERSION_DATE=2023-04-18 +DEFAULT_VERSION_DATE=2023-07-18 DEFAULT_VERSION_CLASSFILE_MAJOR=61 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 diff --git a/make/data/cacerts/gtsrootcar1 b/make/data/cacerts/gtsrootcar1 new file mode 100644 index 00000000000..ac879b232b2 --- /dev/null +++ b/make/data/cacerts/gtsrootcar1 @@ -0,0 +1,38 @@ +Owner: CN=GTS Root R1, O=Google Trust Services LLC, C=US +Issuer: CN=GTS Root R1, O=Google Trust Services LLC, C=US +Serial number: 203e5936f31b01349886ba217 +Valid from: Wed Jun 22 00:00:00 GMT 2016 until: Sun Jun 22 00:00:00 GMT 2036 +Signature algorithm name: SHA384withRSA +Subject Public Key Algorithm: 4096-bit RSA key +Version: 3 +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo +27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w +Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw +TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl +qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH +szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 +Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk +MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p +aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN +VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb +C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy +h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 +7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J +ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef +MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ +Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT +6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ +0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm +2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb +bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c +-----END CERTIFICATE----- \ No newline at end of file diff --git a/make/data/cacerts/gtsrootcar2 b/make/data/cacerts/gtsrootcar2 new file mode 100644 index 00000000000..a71e7c5e951 --- /dev/null +++ b/make/data/cacerts/gtsrootcar2 @@ -0,0 +1,38 @@ +Owner: CN=GTS Root R2, O=Google Trust Services LLC, C=US +Issuer: CN=GTS Root R2, O=Google Trust Services LLC, C=US +Serial number: 203e5aec58d04251aab1125aa +Valid from: Wed Jun 22 00:00:00 GMT 2016 until: Sun Jun 22 00:00:00 GMT 2036 +Signature algorithm name: SHA384withRSA +Subject Public Key Algorithm: 4096-bit RSA key +Version: 3 +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3LvCvpt +nfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY +6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAu +MC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k +RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWg +f9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV ++3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8Yzo +dDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW +Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKa +G73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCq +gc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H +vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8 +0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyC +B19m3H0Q/gxhswWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2u +NmSRXbBoGOqKYcl3qJfEycel/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMg +yALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVnjWQye+mew4K6Ki3pHrTgSAai/Gev +HyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y59PYjJbigapordwj6 +xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M7YNR +TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg +JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV +7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl +6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL +-----END CERTIFICATE----- \ No newline at end of file diff --git a/make/data/cacerts/gtsrootecccar3 b/make/data/cacerts/gtsrootecccar3 new file mode 100644 index 00000000000..38f7533ca58 --- /dev/null +++ b/make/data/cacerts/gtsrootecccar3 @@ -0,0 +1,20 @@ +Owner: CN=GTS Root R3, O=Google Trust Services LLC, C=US +Issuer: CN=GTS Root R3, O=Google Trust Services LLC, C=US +Serial number: 203e5b882eb20f825276d3d66 +Valid from: Wed Jun 22 00:00:00 GMT 2016 until: Sun Jun 22 00:00:00 GMT 2036 +Signature algorithm name: SHA384withECDSA +Subject Public Key Algorithm: 384-bit EC (secp384r1) key +Version: 3 +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G +jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2 +4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7 +VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm +ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X +-----END CERTIFICATE----- \ No newline at end of file diff --git a/make/data/cacerts/gtsrootecccar4 b/make/data/cacerts/gtsrootecccar4 new file mode 100644 index 00000000000..e812a16c24b --- /dev/null +++ b/make/data/cacerts/gtsrootecccar4 @@ -0,0 +1,20 @@ +Owner: CN=GTS Root R4, O=Google Trust Services LLC, C=US +Issuer: CN=GTS Root R4, O=Google Trust Services LLC, C=US +Serial number: 203e5c068ef631a9c72905052 +Valid from: Wed Jun 22 00:00:00 GMT 2016 until: Sun Jun 22 00:00:00 GMT 2036 +Signature algorithm name: SHA384withECDSA +Subject Public Key Algorithm: 384-bit EC (secp384r1) key +Version: 3 +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi +QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR +HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D +9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 +p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD +-----END CERTIFICATE----- \ No newline at end of file diff --git a/make/data/cacerts/microsoftecc2017 b/make/data/cacerts/microsoftecc2017 new file mode 100644 index 00000000000..8fc67332045 --- /dev/null +++ b/make/data/cacerts/microsoftecc2017 @@ -0,0 +1,22 @@ +Owner: CN=Microsoft ECC Root Certificate Authority 2017, O=Microsoft Corporation, C=US +Issuer: CN=Microsoft ECC Root Certificate Authority 2017, O=Microsoft Corporation, C=US +Serial number: 66f23daf87de8bb14aea0c573101c2ec +Valid from: Wed Dec 18 23:06:45 GMT 2019 until: Fri Jul 18 23:16:04 GMT 2042 +Signature algorithm name: SHA384withECDSA +Subject Public Key Algorithm: 384-bit EC (secp384r1) key +Version: 3 +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD +VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw +MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy +b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR +ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb +hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3 +FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV +L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB +iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE----- diff --git a/make/data/cacerts/microsoftrsa2017 b/make/data/cacerts/microsoftrsa2017 new file mode 100644 index 00000000000..2d30c9688b7 --- /dev/null +++ b/make/data/cacerts/microsoftrsa2017 @@ -0,0 +1,40 @@ +Owner: CN=Microsoft RSA Root Certificate Authority 2017, O=Microsoft Corporation, C=US +Issuer: CN=Microsoft RSA Root Certificate Authority 2017, O=Microsoft Corporation, C=US +Serial number: 1ed397095fd8b4b347701eaabe7f45b3 +Valid from: Wed Dec 18 22:51:22 GMT 2019 until: Fri Jul 18 23:00:23 GMT 2042 +Signature algorithm name: SHA384withRSA +Subject Public Key Algorithm: 4096-bit RSA key +Version: 3 +-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl +MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N +aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ +Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0 +ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1 +HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm +gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ +jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc +aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG +YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6 +W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K +UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH ++FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q +W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC +LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC +gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6 +tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh +SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2 +TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3 +pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR +xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp +GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9 +dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN +AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB +RA+GsCyRxj3qrg+E +-----END CERTIFICATE----- diff --git a/make/data/cacerts/twcaglobalrootca b/make/data/cacerts/twcaglobalrootca new file mode 100644 index 00000000000..f17112ad9a5 --- /dev/null +++ b/make/data/cacerts/twcaglobalrootca @@ -0,0 +1,38 @@ +Owner: CN=TWCA Global Root CA, OU=Root CA, O=TAIWAN-CA, C=TW +Issuer: CN=TWCA Global Root CA, OU=Root CA, O=TAIWAN-CA, C=TW +Serial number: cbe +Valid from: Wed Jun 27 06:28:33 GMT 2012 until: Tue Dec 31 15:59:59 GMT 2030 +Signature algorithm name: SHA256withRSA +Subject Public Key Algorithm: 4096-bit RSA key +Version: 3 +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- diff --git a/make/data/charsetmapping/charsets b/make/data/charsetmapping/charsets index a06e4038d69..950347a6202 100644 --- a/make/data/charsetmapping/charsets +++ b/make/data/charsetmapping/charsets @@ -1,5 +1,5 @@ # -# Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -477,6 +477,11 @@ charset x-IBM874 IBM874 alias ibm-874 alias 874 +# alias for GB18030 is generated at runtime +charset GB18030 GB18030 + package sun.nio.cs + type source + ######################################################## # # charsets provided by ExtendedCharsets provider. @@ -564,11 +569,6 @@ charset GBK GBK # Simplified Chinese alias windows-936 alias CP936 -charset GB18030 GB18030 - package sun.nio.cs.ext - type template - alias gb18030-2000 - charset GB2312 EUC_CN package sun.nio.cs.ext type dbcs diff --git a/make/data/charsetmapping/stdcs-aix b/make/data/charsetmapping/stdcs-aix index f17468bbdbc..750308b3bfb 100644 --- a/make/data/charsetmapping/stdcs-aix +++ b/make/data/charsetmapping/stdcs-aix @@ -7,7 +7,6 @@ Big5_HKSCS EUC_CN EUC_KR GBK -GB18030 IBM856 IBM921 IBM922 diff --git a/make/data/charsetmapping/stdcs-linux b/make/data/charsetmapping/stdcs-linux index d7b3b4d9672..b9e80ca6455 100644 --- a/make/data/charsetmapping/stdcs-linux +++ b/make/data/charsetmapping/stdcs-linux @@ -11,7 +11,6 @@ EUC_JP_LINUX EUC_JP_Open EUC_TW GBK -GB18030 ISO_8859_11 ISO_8859_3 ISO_8859_6 diff --git a/make/data/charsetmapping/stdcs-solaris b/make/data/charsetmapping/stdcs-solaris deleted file mode 100644 index cf3c01f12f8..00000000000 --- a/make/data/charsetmapping/stdcs-solaris +++ /dev/null @@ -1,26 +0,0 @@ -# -# generate these charsets into sun.nio.cs -# -Big5 -Big5_Solaris -Big5_HKSCS # always together with Big5 -EUC_CN -EUC_KR -EUC_JP -EUC_JP_LINUX -EUC_JP_Open -EUC_TW -GBK -GB18030 -ISO_8859_11 -ISO_8859_3 -ISO_8859_6 -ISO_8859_8 -Johab -PCK -TIS_620 -JIS_X_0201 -JIS_X_0208 -JIS_X_0212 -JIS_X_0208_Solaris -JIS_X_0212_Solaris diff --git a/make/data/charsetmapping/stdcs-windows b/make/data/charsetmapping/stdcs-windows index 3185e9a966d..482b699bcd1 100644 --- a/make/data/charsetmapping/stdcs-windows +++ b/make/data/charsetmapping/stdcs-windows @@ -2,7 +2,6 @@ # generate these charsets into sun.nio.cs # GBK -GB18030 Johab MS1255 MS1256 diff --git a/make/data/currency/CurrencyData.properties b/make/data/currency/CurrencyData.properties index d234c96c476..12c0c69801e 100644 --- a/make/data/currency/CurrencyData.properties +++ b/make/data/currency/CurrencyData.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,7 @@ formatVersion=3 # Version of the currency code information in this class. # It is a serial number that accompanies with each amendment. -dataVersion=174 +dataVersion=175 # List of all valid ISO 4217 currency codes. # To ensure compatibility, do not remove codes. diff --git a/make/data/tzdata/VERSION b/make/data/tzdata/VERSION index 0f328a4a7ff..66bd061e8bc 100644 --- a/make/data/tzdata/VERSION +++ b/make/data/tzdata/VERSION @@ -21,4 +21,4 @@ # or visit www.oracle.com if you need additional information or have any # questions. # -tzdata2022g +tzdata2023c diff --git a/make/data/tzdata/africa b/make/data/tzdata/africa index 830d7d10b7e..a73405fdb01 100644 --- a/make/data/tzdata/africa +++ b/make/data/tzdata/africa @@ -344,6 +344,14 @@ Rule Egypt 2007 only - Sep Thu>=1 24:00 0 - # From Mina Samuel (2016-07-04): # Egyptian government took the decision to cancel the DST, +# From Ahmad ElDardiry (2023-03-01): +# Egypt officially announced today that daylight savings will be +# applied from last Friday of April to last Thursday of October. +# From Paul Eggert (2023-03-01): +# Assume transitions are at 00:00 and 24:00 respectively. +# From Amir Adib (2023-03-07): +# https://www.facebook.com/EgyptianCabinet/posts/638829614954129/ + Rule Egypt 2008 only - Aug lastThu 24:00 0 - Rule Egypt 2009 only - Aug 20 24:00 0 - Rule Egypt 2010 only - Aug 10 24:00 0 - @@ -353,6 +361,8 @@ Rule Egypt 2014 only - May 15 24:00 1:00 S Rule Egypt 2014 only - Jun 26 24:00 0 - Rule Egypt 2014 only - Jul 31 24:00 1:00 S Rule Egypt 2014 only - Sep lastThu 24:00 0 - +Rule Egypt 2023 max - Apr lastFri 0:00 1:00 S +Rule Egypt 2023 max - Oct lastThu 24:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] #STDOFF 2:05:08.9 @@ -452,7 +462,7 @@ Zone Africa/Nairobi 2:27:16 - LMT 1908 May # President William R. Tolbert, Jr., July 23, 1971-July 31, 1972. # Monrovia: Executive Mansion. # -# Use the abbreviation "MMT" before 1972, as the more-accurate numeric +# Use the abbreviation "MMT" before 1972, as the more accurate numeric # abbreviation "-004430" would be one byte over the POSIX limit. # # Zone NAME STDOFF RULES FORMAT [UNTIL] @@ -589,8 +599,8 @@ Zone Africa/Tripoli 0:52:44 - LMT 1920 # DST the coming summer... # # Some sources, in French: -# http://www.defimedia.info/news/946/Rashid-Beebeejaun-:-%C2%AB-L%E2%80%99heure-d%E2%80%99%C3%A9t%C3%A9-ne-sera-pas-appliqu%C3%A9e-cette-ann%C3%A9e-%C2%BB -# http://lexpress.mu/Story/3398~Beebeejaun---Les-objectifs-d-%C3%A9conomie-d-%C3%A9nergie-de-l-heure-d-%C3%A9t%C3%A9-ont-%C3%A9t%C3%A9-atteints- +# http://www.defimedia.info/news/946/Rashid-Beebeejaun-:-«-L%E2%80%99heure-d%E2%80%99été-ne-sera-pas-appliquée-cette-année-» +# http://lexpress.mu/Story/3398~Beebeejaun---Les-objectifs-d-économie-d-énergie-de-l-heure-d-été-ont-été-atteints- # # Our wrap-up: # https://www.timeanddate.com/news/time/mauritius-dst-will-not-repeat.html @@ -721,7 +731,7 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis # More articles in the press # https://www.yabiladi.com/articles/details/5058/secret-l-heure-d-ete-maroc-leve.html # http://www.lematin.ma/Actualite/Express/Article.asp?id=148923 -# http://www.lavieeco.com/actualite/Le-Maroc-passe-sur-GMT%2B1-a-partir-de-dim +# http://www.lavieeco.com/actualite/Le-Maroc-passe-sur-GMT+1-a-partir-de-dim # From Petr Machata (2011-03-30): # They have it written in English here: @@ -736,7 +746,7 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis # According to Infomédiaire web site from Morocco (infomediaire.ma), # on March 9, 2012, (in French) Heure légale: # Le Maroc adopte officiellement l'heure d'été -# http://www.infomediaire.ma/news/maroc/heure-l%C3%A9gale-le-maroc-adopte-officiellement-lheure-d%C3%A9t%C3%A9 +# http://www.infomediaire.ma/news/maroc/heure-légale-le-maroc-adopte-officiellement-lheure-dété # Governing Council adopted draft decree, that Morocco DST starts on # the last Sunday of March (March 25, 2012) and ends on # last Sunday of September (September 30, 2012) @@ -860,19 +870,28 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis # Friday or Saturday (and so the 2 days off are on a weekend), the next time # shift will be the next weekend. # -# From Paul Eggert (2020-05-31): +# From Milamber (2021-03-31, 2022-03-10): +# https://www.mmsp.gov.ma/fr/actualites.aspx?id=2076 +# https://www.ecoactu.ma/horaires-administration-ramadan-gmtheure-gmt-a-partir-de-dimanche-27-mars/ +# +# From Milamber (2023-03-14, 2023-03-15): +# The return to legal GMT time will take place this Sunday, March 19 at 3 a.m. +# ... the return to GMT+1 will be made on Sunday April 23, 2023 at 2 a.m. +# https://www.mmsp.gov.ma/fr/actualites/passage-à-l%E2%80%99heure-gmt-à-partir-du-dimanche-19-mars-2023 +# +# From Paul Eggert (2023-03-14): # For now, guess that in the future Morocco will fall back at 03:00 # the last Sunday before Ramadan, and spring forward at 02:00 the -# first Sunday after two days after Ramadan. To implement this, +# first Sunday after one day after Ramadan. To implement this, # transition dates and times for 2019 through 2087 were determined by -# running the following program under GNU Emacs 26.3. (This algorithm +# running the following program under GNU Emacs 28.2. (This algorithm # also produces the correct transition dates for 2016 through 2018, # though the times differ due to Morocco's time zone change in 2018.) # (let ((islamic-year 1440)) # (require 'cal-islam) # (while (< islamic-year 1511) # (let ((a (calendar-islamic-to-absolute (list 9 1 islamic-year))) -# (b (+ 2 (calendar-islamic-to-absolute (list 10 1 islamic-year)))) +# (b (+ 1 (calendar-islamic-to-absolute (list 10 1 islamic-year)))) # (sunday 0)) # (while (/= sunday (mod (setq a (1- a)) 7))) # (while (/= sunday (mod b 7)) @@ -886,10 +905,6 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis # (car (cdr (cdr a))) (calendar-month-name (car a) t) (car (cdr a)) # (car (cdr (cdr b))) (calendar-month-name (car b) t) (car (cdr b))))) # (setq islamic-year (+ 1 islamic-year)))) -# -# From Milamber (2021-03-31, 2022-03-10), confirming these predictions: -# https://www.mmsp.gov.ma/fr/actualites.aspx?id=2076 -# https://www.ecoactu.ma/horaires-administration-ramadan-gmtheure-gmt-a-partir-de-dimanche-27-mars/ # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Morocco 1939 only - Sep 12 0:00 1:00 - @@ -942,7 +957,7 @@ Rule Morocco 2021 only - May 16 2:00 0 - Rule Morocco 2022 only - Mar 27 3:00 -1:00 - Rule Morocco 2022 only - May 8 2:00 0 - Rule Morocco 2023 only - Mar 19 3:00 -1:00 - -Rule Morocco 2023 only - Apr 30 2:00 0 - +Rule Morocco 2023 only - Apr 23 2:00 0 - Rule Morocco 2024 only - Mar 10 3:00 -1:00 - Rule Morocco 2024 only - Apr 14 2:00 0 - Rule Morocco 2025 only - Feb 23 3:00 -1:00 - @@ -958,7 +973,7 @@ Rule Morocco 2029 only - Feb 18 2:00 0 - Rule Morocco 2029 only - Dec 30 3:00 -1:00 - Rule Morocco 2030 only - Feb 10 2:00 0 - Rule Morocco 2030 only - Dec 22 3:00 -1:00 - -Rule Morocco 2031 only - Feb 2 2:00 0 - +Rule Morocco 2031 only - Jan 26 2:00 0 - Rule Morocco 2031 only - Dec 14 3:00 -1:00 - Rule Morocco 2032 only - Jan 18 2:00 0 - Rule Morocco 2032 only - Nov 28 3:00 -1:00 - @@ -974,7 +989,7 @@ Rule Morocco 2036 only - Nov 23 2:00 0 - Rule Morocco 2037 only - Oct 4 3:00 -1:00 - Rule Morocco 2037 only - Nov 15 2:00 0 - Rule Morocco 2038 only - Sep 26 3:00 -1:00 - -Rule Morocco 2038 only - Nov 7 2:00 0 - +Rule Morocco 2038 only - Oct 31 2:00 0 - Rule Morocco 2039 only - Sep 18 3:00 -1:00 - Rule Morocco 2039 only - Oct 23 2:00 0 - Rule Morocco 2040 only - Sep 2 3:00 -1:00 - @@ -990,7 +1005,7 @@ Rule Morocco 2044 only - Aug 28 2:00 0 - Rule Morocco 2045 only - Jul 9 3:00 -1:00 - Rule Morocco 2045 only - Aug 20 2:00 0 - Rule Morocco 2046 only - Jul 1 3:00 -1:00 - -Rule Morocco 2046 only - Aug 12 2:00 0 - +Rule Morocco 2046 only - Aug 5 2:00 0 - Rule Morocco 2047 only - Jun 23 3:00 -1:00 - Rule Morocco 2047 only - Jul 28 2:00 0 - Rule Morocco 2048 only - Jun 7 3:00 -1:00 - @@ -1006,7 +1021,7 @@ Rule Morocco 2052 only - Jun 2 2:00 0 - Rule Morocco 2053 only - Apr 13 3:00 -1:00 - Rule Morocco 2053 only - May 25 2:00 0 - Rule Morocco 2054 only - Apr 5 3:00 -1:00 - -Rule Morocco 2054 only - May 17 2:00 0 - +Rule Morocco 2054 only - May 10 2:00 0 - Rule Morocco 2055 only - Mar 28 3:00 -1:00 - Rule Morocco 2055 only - May 2 2:00 0 - Rule Morocco 2056 only - Mar 12 3:00 -1:00 - @@ -1022,7 +1037,7 @@ Rule Morocco 2060 only - Mar 7 2:00 0 - Rule Morocco 2061 only - Jan 16 3:00 -1:00 - Rule Morocco 2061 only - Feb 27 2:00 0 - Rule Morocco 2062 only - Jan 8 3:00 -1:00 - -Rule Morocco 2062 only - Feb 19 2:00 0 - +Rule Morocco 2062 only - Feb 12 2:00 0 - Rule Morocco 2062 only - Dec 31 3:00 -1:00 - Rule Morocco 2063 only - Feb 4 2:00 0 - Rule Morocco 2063 only - Dec 16 3:00 -1:00 - @@ -1038,7 +1053,7 @@ Rule Morocco 2067 only - Dec 11 2:00 0 - Rule Morocco 2068 only - Oct 21 3:00 -1:00 - Rule Morocco 2068 only - Dec 2 2:00 0 - Rule Morocco 2069 only - Oct 13 3:00 -1:00 - -Rule Morocco 2069 only - Nov 24 2:00 0 - +Rule Morocco 2069 only - Nov 17 2:00 0 - Rule Morocco 2070 only - Oct 5 3:00 -1:00 - Rule Morocco 2070 only - Nov 9 2:00 0 - Rule Morocco 2071 only - Sep 20 3:00 -1:00 - @@ -1054,7 +1069,7 @@ Rule Morocco 2075 only - Sep 15 2:00 0 - Rule Morocco 2076 only - Jul 26 3:00 -1:00 - Rule Morocco 2076 only - Sep 6 2:00 0 - Rule Morocco 2077 only - Jul 18 3:00 -1:00 - -Rule Morocco 2077 only - Aug 29 2:00 0 - +Rule Morocco 2077 only - Aug 22 2:00 0 - Rule Morocco 2078 only - Jul 10 3:00 -1:00 - Rule Morocco 2078 only - Aug 14 2:00 0 - Rule Morocco 2079 only - Jun 25 3:00 -1:00 - @@ -1064,13 +1079,13 @@ Rule Morocco 2080 only - Jul 21 2:00 0 - Rule Morocco 2081 only - Jun 1 3:00 -1:00 - Rule Morocco 2081 only - Jul 13 2:00 0 - Rule Morocco 2082 only - May 24 3:00 -1:00 - -Rule Morocco 2082 only - Jul 5 2:00 0 - +Rule Morocco 2082 only - Jun 28 2:00 0 - Rule Morocco 2083 only - May 16 3:00 -1:00 - Rule Morocco 2083 only - Jun 20 2:00 0 - Rule Morocco 2084 only - Apr 30 3:00 -1:00 - Rule Morocco 2084 only - Jun 11 2:00 0 - Rule Morocco 2085 only - Apr 22 3:00 -1:00 - -Rule Morocco 2085 only - Jun 3 2:00 0 - +Rule Morocco 2085 only - May 27 2:00 0 - Rule Morocco 2086 only - Apr 14 3:00 -1:00 - Rule Morocco 2086 only - May 19 2:00 0 - Rule Morocco 2087 only - Mar 30 3:00 -1:00 - @@ -1213,15 +1228,15 @@ Zone Africa/Windhoek 1:08:24 - LMT 1892 Feb 8 # From P Chan (2020-12-03): # GMT was adopted as the standard time of Lagos on 1905-07-01. # Lagos Weekly Record, 1905-06-24, p 3 -# http://ddsnext.crl.edu/titles/31558#?c=0&m=668&s=0&cv=2&r=0&xywh=1446%2C5221%2C1931%2C1235 +# http://ddsnext.crl.edu/titles/31558#?c=0&m=668&s=0&cv=2&r=0&xywh=1446,5221,1931,1235 # says "It is officially notified that on and after the 1st of July 1905 -# Greenwich Mean Solar Time will be adopted thought the Colony and +# Greenwich Mean Solar Time will be adopted throughout the Colony and # Protectorate, and that it will be necessary to put all clocks 13 minutes and # 35 seconds back, recording local mean time." # # It seemed that Lagos returned to LMT on 1908-07-01. # [The Lagos Standard], 1908-07-01, p 5 -# http://ddsnext.crl.edu/titles/31556#?c=0&m=78&s=0&cv=4&r=0&xywh=-92%2C3590%2C3944%2C2523 +# http://ddsnext.crl.edu/titles/31556#?c=0&m=78&s=0&cv=4&r=0&xywh=-92,3590,3944,2523 # says "Scarcely have the people become accustomed to this new time, when # another official notice has now appeared announcing that from and after the # 1st July next, return will be made to local mean time." @@ -1233,7 +1248,7 @@ Zone Africa/Windhoek 1:08:24 - LMT 1892 Feb 8 # https://libsysdigi.library.illinois.edu/ilharvest/Africana/Books2011-05/3064634/3064634_1914/3064634_1914_opt.pdf#page=27 # "On January 1st [1914], a universal standard time for Nigeria was adopted, # viz., half an hour fast on Greenwich mean time, corresponding to the meridian -# 7 [degrees] 30' E. long." +# 7° 30' E. long." # Lloyd's Register of Shipping (1915) says "Hitherto the time observed in Lagos # was the local mean time. On 1st January, 1914, standard time for the whole of # Nigeria was introduced ... Lagos time has been advanced about 16 minutes @@ -1251,7 +1266,7 @@ Zone Africa/Windhoek 1:08:24 - LMT 1892 Feb 8 # The Lagos Weekly Record, 1919-09-20, p 3 details discussion on the first # reading of this Bill by the Legislative Council of the Colony of Nigeria on # Thursday 1919-08-28: -# http://ddsnext.crl.edu/titles/31558?terms&item_id=303484#?m=1118&c=1&s=0&cv=2&r=0&xywh=1261%2C3408%2C2994%2C1915 +# http://ddsnext.crl.edu/titles/31558?terms&item_id=303484#?m=1118&c=1&s=0&cv=2&r=0&xywh=1261,3408,2994,1915 # "The proposal is that the Globe should be divided into twelve zones East and # West of Greenwich, of one hour each, Nigeria falling into the zone with a # standard of one hour fast on Greenwich Mean Time. Nigeria standard time is diff --git a/make/data/tzdata/antarctica b/make/data/tzdata/antarctica index 792542b9224..3de5e726eb4 100644 --- a/make/data/tzdata/antarctica +++ b/make/data/tzdata/antarctica @@ -315,7 +315,7 @@ Zone Antarctica/Rothera 0 - -00 1976 Dec 1 # but that he found it more convenient to keep GMT+12 # as supplies for the station were coming from McMurdo Sound, # which was on GMT+12 because New Zealand was on GMT+12 all year -# at that time (1957). (Source: Siple's book 90 Degrees South.) +# at that time (1957). (Source: Siple's book 90° South.) # # From Susan Smith # http://www.cybertours.com/whs/pole10.html diff --git a/make/data/tzdata/asia b/make/data/tzdata/asia index ff81978bc47..6a048c3ad28 100644 --- a/make/data/tzdata/asia +++ b/make/data/tzdata/asia @@ -2714,6 +2714,40 @@ Zone Asia/Pyongyang 8:23:00 - LMT 1908 Apr 1 # Lebanon +# +# From Saadallah Itani (2023-03-23): +# Lebanon ... announced today delay of Spring forward from March 25 to April 20. +# +# From Paul Eggert (2023-03-27): +# This announcement was by the Lebanese caretaker prime minister Najib Mikati. +# https://www.mtv.com.lb/en/News/Local/1352516/lebanon-postpones-daylight-saving-time-adoption +# A video was later leaked to the media of parliament speaker Nabih Berri +# asking Mikati to postpone DST to aid observance of Ramadan, Mikati objecting +# that this would cause problems such as scheduling airline flights, to which +# Berri interjected, "What flights?" +# +# The change was controversial and led to a partly-sectarian divide. +# Many Lebanese institutions, including the education ministry, the Maronite +# church, and two news channels LCBI and MTV, ignored the announcement and +# went ahead with the long-scheduled spring-forward on March 25/26, some +# arguing that the prime minister had not followed the law because the change +# had not been approved by the cabinet. Google went with the announcement; +# Apple ignored it. At least one bank followed the announcement for its doors, +# but ignored the announcement in internal computer systems. +# Beirut international airport listed two times for each departure. +# Dan Azzi wrote "My view is that this whole thing is a Dumb and Dumber movie." +# Eventually the prime minister backed down, said the cabinet had decided to +# stick with its 1998 decision, and that DST would begin midnight March 29/30. +# https://www.nna-leb.gov.lb/en/miscellaneous/604093/lebanon-has-two-times-of-day-amid-daylight-savings +# https://www.cnbc.com/2023/03/27/lebanon-in-two-different-time-zones-as-government-disagrees-on-daylight-savings.html +# +# Although we could model the chaos with two Zones, that would likely cause +# more trouble than it would cure. Since so many manual clocks and +# computer-based timestamps ignored the announcement, stick with official +# cabinet resolutions in the data while recording the prime minister's +# announcement as a comment. This is how we treated a similar situation in +# Rio de Janeiro in spring 1993. +# # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Lebanon 1920 only - Mar 28 0:00 1:00 S Rule Lebanon 1920 only - Oct 25 0:00 0 - @@ -2739,6 +2773,10 @@ Rule Lebanon 1992 only - Oct 4 0:00 0 - Rule Lebanon 1993 max - Mar lastSun 0:00 1:00 S Rule Lebanon 1993 1998 - Sep lastSun 0:00 0 - Rule Lebanon 1999 max - Oct lastSun 0:00 0 - +# This one-time rule, announced by the prime minister first for April 21 +# then for March 30, is commented out for reasons described above. +#Rule Lebanon 2023 only - Mar 30 0:00 1:00 S + # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Beirut 2:22:00 - LMT 1880 2:00 Lebanon EE%sT @@ -2977,7 +3015,7 @@ Zone Asia/Kathmandu 5:41:16 - LMT 1920 # 9pm and moving clocks forward by one hour for the next three months. ...." # # http://www.worldtimezone.com/dst_news/dst_news_pakistan01.html -# http://www.dailytimes.com.pk/default.asp?page=2008%5C05%5C15%5Cstory_15-5-2008_pg1_4 +# http://www.dailytimes.com.pk/default.asp?page=2008\05\15\story_15-5-2008_pg1_4 # From Arthur David Olson (2008-05-19): # XXX--midnight transitions is a guess; 2008 only is a guess. @@ -3300,7 +3338,7 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 # Some of many sources in Arabic: # http://www.samanews.com/index.php?act=Show&id=122638 # -# http://safa.ps/details/news/74352/%D8%A8%D8%AF%D8%A1-%D8%A7%D9%84%D8%AA%D9%88%D9%82%D9%8A%D8%AA-%D8%A7%D9%84%D8%B5%D9%8A%D9%81%D9%8A-%D8%A8%D8%A7%D9%84%D8%B6%D9%81%D8%A9-%D9%88%D8%BA%D8%B2%D8%A9-%D9%84%D9%8A%D9%84%D8%A9-%D8%A7%D9%84%D8%AC%D9%85%D8%B9%D8%A9.html +# http://safa.ps/details/news/74352/بدء-التوقيت-الصيفي-بالضفة-وغزة-ليلة-الجمعة.html # # Our brief summary: # https://www.timeanddate.com/news/time/gaza-west-bank-dst-2012.html @@ -3310,7 +3348,7 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 # time from midnight on Friday, March 29, 2013" (translated). # [These are in Arabic and are for Gaza and for Ramallah, respectively.] # http://www.samanews.com/index.php?act=Show&id=154120 -# http://safa.ps/details/news/99844/%D8%B1%D8%A7%D9%85-%D8%A7%D9%84%D9%84%D9%87-%D8%A8%D8%AF%D8%A1-%D8%A7%D9%84%D8%AA%D9%88%D9%82%D9%8A%D8%AA-%D8%A7%D9%84%D8%B5%D9%8A%D9%81%D9%8A-29-%D8%A7%D9%84%D8%AC%D8%A7%D8%B1%D9%8A.html +# http://safa.ps/details/news/99844/رام-الله-بدء-التوقيت-الصيفي-29-الجاري.html # From Steffen Thorsen (2013-09-24): # The Gaza and West Bank are ending DST Thursday at midnight @@ -3408,9 +3446,41 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 # (2022-08-31): ... the Saturday before the last Sunday in March and October # at 2:00 AM ,for the years from 2023 to 2026. # (2022-09-05): https://mtit.pna.ps/Site/New/1453 -# -# From Paul Eggert (2022-08-31): -# For now, assume that this rule will also be used after 2026. + +# From Heba Hamad (2023-03-22): +# ... summer time will begin in Palestine from Saturday 04-29-2023, +# 02:00 AM by 60 minutes forward. +# +# From Paul Eggert (2023-03-22): +# For now, guess that spring and fall transitions will normally +# continue to use 2022's rules, that during DST Palestine will switch +# to standard time at 02:00 the last Saturday before Ramadan and back +# to DST at 02:00 the first Saturday after Ramadan, and that +# if the normal spring-forward or fall-back transition occurs during +# Ramadan the former is delayed and the latter advanced. +# To implement this, I predicted Ramadan-oriented transition dates for +# 2023 through 2086 by running the following program under GNU Emacs 28.2, +# with the results integrated by hand into the table below. +# Predictions after 2086 are approximated without Ramadan. +# +# (let ((islamic-year 1444)) +# (require 'cal-islam) +# (while (< islamic-year 1510) +# (let ((a (calendar-islamic-to-absolute (list 9 1 islamic-year))) +# (b (+ 1 (calendar-islamic-to-absolute (list 10 1 islamic-year)))) +# (saturday 6)) +# (while (/= saturday (mod (setq a (1- a)) 7))) +# (while (/= saturday (mod b 7)) +# (setq b (1+ b))) +# (setq a (calendar-gregorian-from-absolute a)) +# (setq b (calendar-gregorian-from-absolute b)) +# (insert +# (format +# (concat "Rule Palestine\t%d\tonly\t-\t%s\t%2d\t2:00\t0\t-\n" +# "Rule Palestine\t%d\tonly\t-\t%s\t%2d\t2:00\t1:00\tS\n") +# (car (cdr (cdr a))) (calendar-month-name (car a) t) (car (cdr a)) +# (car (cdr (cdr b))) (calendar-month-name (car b) t) (car (cdr b))))) +# (setq islamic-year (+ 1 islamic-year)))) # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule EgyptAsia 1957 only - May 10 0:00 1:00 S @@ -3450,8 +3520,86 @@ Rule Palestine 2020 2021 - Mar Sat<=30 0:00 1:00 S Rule Palestine 2020 only - Oct 24 1:00 0 - Rule Palestine 2021 only - Oct 29 1:00 0 - Rule Palestine 2022 only - Mar 27 0:00 1:00 S -Rule Palestine 2022 max - Oct Sat<=30 2:00 0 - -Rule Palestine 2023 max - Mar Sat<=30 2:00 1:00 S +Rule Palestine 2022 2035 - Oct Sat<=30 2:00 0 - +Rule Palestine 2023 only - Apr 29 2:00 1:00 S +Rule Palestine 2024 only - Apr 13 2:00 1:00 S +Rule Palestine 2025 only - Apr 5 2:00 1:00 S +Rule Palestine 2026 2054 - Mar Sat<=30 2:00 1:00 S +Rule Palestine 2036 only - Oct 18 2:00 0 - +Rule Palestine 2037 only - Oct 10 2:00 0 - +Rule Palestine 2038 only - Sep 25 2:00 0 - +Rule Palestine 2039 only - Sep 17 2:00 0 - +Rule Palestine 2039 only - Oct 22 2:00 1:00 S +Rule Palestine 2039 2067 - Oct Sat<=30 2:00 0 - +Rule Palestine 2040 only - Sep 1 2:00 0 - +Rule Palestine 2040 only - Oct 13 2:00 1:00 S +Rule Palestine 2041 only - Aug 24 2:00 0 - +Rule Palestine 2041 only - Sep 28 2:00 1:00 S +Rule Palestine 2042 only - Aug 16 2:00 0 - +Rule Palestine 2042 only - Sep 20 2:00 1:00 S +Rule Palestine 2043 only - Aug 1 2:00 0 - +Rule Palestine 2043 only - Sep 12 2:00 1:00 S +Rule Palestine 2044 only - Jul 23 2:00 0 - +Rule Palestine 2044 only - Aug 27 2:00 1:00 S +Rule Palestine 2045 only - Jul 15 2:00 0 - +Rule Palestine 2045 only - Aug 19 2:00 1:00 S +Rule Palestine 2046 only - Jun 30 2:00 0 - +Rule Palestine 2046 only - Aug 11 2:00 1:00 S +Rule Palestine 2047 only - Jun 22 2:00 0 - +Rule Palestine 2047 only - Jul 27 2:00 1:00 S +Rule Palestine 2048 only - Jun 6 2:00 0 - +Rule Palestine 2048 only - Jul 18 2:00 1:00 S +Rule Palestine 2049 only - May 29 2:00 0 - +Rule Palestine 2049 only - Jul 3 2:00 1:00 S +Rule Palestine 2050 only - May 21 2:00 0 - +Rule Palestine 2050 only - Jun 25 2:00 1:00 S +Rule Palestine 2051 only - May 6 2:00 0 - +Rule Palestine 2051 only - Jun 17 2:00 1:00 S +Rule Palestine 2052 only - Apr 27 2:00 0 - +Rule Palestine 2052 only - Jun 1 2:00 1:00 S +Rule Palestine 2053 only - Apr 12 2:00 0 - +Rule Palestine 2053 only - May 24 2:00 1:00 S +Rule Palestine 2054 only - Apr 4 2:00 0 - +Rule Palestine 2054 only - May 16 2:00 1:00 S +Rule Palestine 2055 only - May 1 2:00 1:00 S +Rule Palestine 2056 only - Apr 22 2:00 1:00 S +Rule Palestine 2057 only - Apr 7 2:00 1:00 S +Rule Palestine 2058 max - Mar Sat<=30 2:00 1:00 S +Rule Palestine 2068 only - Oct 20 2:00 0 - +Rule Palestine 2069 only - Oct 12 2:00 0 - +Rule Palestine 2070 only - Oct 4 2:00 0 - +Rule Palestine 2071 only - Sep 19 2:00 0 - +Rule Palestine 2072 only - Sep 10 2:00 0 - +Rule Palestine 2072 only - Oct 15 2:00 1:00 S +Rule Palestine 2073 only - Sep 2 2:00 0 - +Rule Palestine 2073 only - Oct 7 2:00 1:00 S +Rule Palestine 2074 only - Aug 18 2:00 0 - +Rule Palestine 2074 only - Sep 29 2:00 1:00 S +Rule Palestine 2075 only - Aug 10 2:00 0 - +Rule Palestine 2075 only - Sep 14 2:00 1:00 S +Rule Palestine 2075 max - Oct Sat<=30 2:00 0 - +Rule Palestine 2076 only - Jul 25 2:00 0 - +Rule Palestine 2076 only - Sep 5 2:00 1:00 S +Rule Palestine 2077 only - Jul 17 2:00 0 - +Rule Palestine 2077 only - Aug 28 2:00 1:00 S +Rule Palestine 2078 only - Jul 9 2:00 0 - +Rule Palestine 2078 only - Aug 13 2:00 1:00 S +Rule Palestine 2079 only - Jun 24 2:00 0 - +Rule Palestine 2079 only - Aug 5 2:00 1:00 S +Rule Palestine 2080 only - Jun 15 2:00 0 - +Rule Palestine 2080 only - Jul 20 2:00 1:00 S +Rule Palestine 2081 only - Jun 7 2:00 0 - +Rule Palestine 2081 only - Jul 12 2:00 1:00 S +Rule Palestine 2082 only - May 23 2:00 0 - +Rule Palestine 2082 only - Jul 4 2:00 1:00 S +Rule Palestine 2083 only - May 15 2:00 0 - +Rule Palestine 2083 only - Jun 19 2:00 1:00 S +Rule Palestine 2084 only - Apr 29 2:00 0 - +Rule Palestine 2084 only - Jun 10 2:00 1:00 S +Rule Palestine 2085 only - Apr 21 2:00 0 - +Rule Palestine 2085 only - Jun 2 2:00 1:00 S +Rule Palestine 2086 only - Apr 13 2:00 0 - +Rule Palestine 2086 only - May 18 2:00 1:00 S # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Asia/Gaza 2:17:52 - LMT 1900 Oct @@ -3655,7 +3803,7 @@ Zone Asia/Singapore 6:55:25 - LMT 1901 Jan 1 # standard time is SLST. # # From Paul Eggert (2016-10-18): -# "SLST" seems to be reasonably recent and rarely-used outside time +# "SLST" seems to be reasonably recent and rarely used outside time # zone nerd sources. I searched Google News and found three uses of # it in the International Business Times of India in February and # March of this year when discussing cricket match times, but nothing diff --git a/make/data/tzdata/australasia b/make/data/tzdata/australasia index fbe3b8a6d72..893d7055eab 100644 --- a/make/data/tzdata/australasia +++ b/make/data/tzdata/australasia @@ -346,7 +346,7 @@ Zone Antarctica/Macquarie 0 - -00 1899 Nov # From Steffen Thorsen (2013-01-10): # Fiji will end DST on 2014-01-19 02:00: -# http://www.fiji.gov.fj/Media-Center/Press-Releases/DAYLIGHT-SAVINGS-TO-END-THIS-MONTH-%281%29.aspx +# http://www.fiji.gov.fj/Media-Center/Press-Releases/DAYLIGHT-SAVINGS-TO-END-THIS-MONTH-(1).aspx # From Ken Rylander (2014-10-20): # DST will start Nov. 2 this year. @@ -746,7 +746,7 @@ Zone Pacific/Pago_Pago 12:37:12 - LMT 1892 Jul 5 # # Samoa's Daylight Saving Time Act 2009 is available here, but does not # contain any dates: -# http://www.parliament.gov.ws/documents/acts/Daylight%20Saving%20Act%20%202009%20%28English%29%20-%20Final%207-7-091.pdf +# http://www.parliament.gov.ws/documents/acts/Daylight%20Saving%20Act%20%202009%20(English)%20-%20Final%207-7-091.pdf # From Laupue Raymond Hughes (2010-10-07): # Please see @@ -1831,7 +1831,7 @@ Zone Pacific/Efate 11:13:16 - LMT 1912 Jan 13 # Vila # period. It would probably be reasonable to assume Guam use GMT+9 during # that period of time like the surrounding area. -# From Paul Eggert (2018-11-18): +# From Paul Eggert (2023-01-23): # Howse writes (p 153) "The Spaniards, on the other hand, reached the # Philippines and the Ladrones from America," and implies that the Ladrones # (now called the Marianas) kept American date for quite some time. @@ -1844,7 +1844,7 @@ Zone Pacific/Efate 11:13:16 - LMT 1912 Jan 13 # Vila # they did as that avoids the need for a separate zone due to our 1970 cutoff. # # US Public Law 106-564 (2000-12-23) made UT +10 the official standard time, -# under the name "Chamorro Standard Time". There is no official abbreviation, +# under the name "Chamorro standard time". There is no official abbreviation, # but Congressman Robert A. Underwood, author of the bill that became law, # wrote in a press release (2000-12-27) that he will seek the use of "ChST". @@ -2222,24 +2222,18 @@ Zone Pacific/Efate 11:13:16 - LMT 1912 Jan 13 # Vila # an international standard, there are some places on the high seas where the # correct date is ambiguous. -# From Wikipedia (2005-08-31): -# Before 1920, all ships kept local apparent time on the high seas by setting -# their clocks at night or at the morning sight so that, given the ship's -# speed and direction, it would be 12 o'clock when the Sun crossed the ship's -# meridian (12 o'clock = local apparent noon). During 1917, at the -# Anglo-French Conference on Time-keeping at Sea, it was recommended that all -# ships, both military and civilian, should adopt hourly standard time zones -# on the high seas. Whenever a ship was within the territorial waters of any -# nation it would use that nation's standard time. The captain was permitted -# to change his ship's clocks at a time of his choice following his ship's -# entry into another zone time - he often chose midnight. These zones were -# adopted by all major fleets between 1920 and 1925 but not by many -# independent merchant ships until World War II. - -# From Paul Eggert, using references suggested by Oscar van Vlijmen -# (2005-03-20): -# -# The American Practical Navigator (2002) -# http://pollux.nss.nima.mil/pubs/pubs_j_apn_sections.html?rid=187 -# talks only about the 180-degree meridian with respect to ships in -# international waters; it ignores the international date line. +# From Wikipedia (2023-01-23): +# The nautical time zone system is analogous to the terrestrial time zone +# system for use on high seas. Under the system time changes are required for +# changes of longitude in one-hour steps. The one-hour step corresponds to a +# time zone width of 15° longitude. The 15° gore that is offset from GMT or +# UT1 (not UTC) by twelve hours is bisected by the nautical date line into two +# 7°30' gores that differ from GMT by ±12 hours. A nautical date line is +# implied but not explicitly drawn on time zone maps. It follows the 180th +# meridian except where it is interrupted by territorial waters adjacent to +# land, forming gaps: it is a pole-to-pole dashed line. + +# From Paul Eggert (2023-01-23): +# The American Practical Navigator , +# 2019 edition, merely says that the International Date Line +# "coincides with the 180th meridian over most of its length." diff --git a/make/data/tzdata/backward b/make/data/tzdata/backward index fa44f655009..c0746d6dd1b 100644 --- a/make/data/tzdata/backward +++ b/make/data/tzdata/backward @@ -297,6 +297,7 @@ Link America/Argentina/Cordoba America/Rosario Link America/Tijuana America/Santa_Isabel Link America/Denver America/Shiprock Link America/Toronto America/Thunder_Bay +Link America/Edmonton America/Yellowknife Link Pacific/Auckland Antarctica/South_Pole Link Asia/Shanghai Asia/Chongqing Link Asia/Shanghai Asia/Harbin diff --git a/make/data/tzdata/europe b/make/data/tzdata/europe index acc5da3ec79..446d2e1e658 100644 --- a/make/data/tzdata/europe +++ b/make/data/tzdata/europe @@ -540,9 +540,7 @@ Zone Europe/London -0:01:15 - LMT 1847 Dec 1 # other form with a traditional approximation for Irish timestamps # after 1971-10-31 02:00 UTC; although this approximation has tm_isdst # flags that are reversed, its UTC offsets are correct and this often -# suffices. This source file currently uses only nonnegative SAVE -# values, but this is intended to change and downstream code should -# not rely on it. +# suffices.... # # The following is like GB-Eire and EU, except with standard time in # summer and negative daylight saving time in winter. It is for when @@ -1136,19 +1134,18 @@ Zone Atlantic/Faroe -0:27:04 - LMT 1908 Jan 11 # Tórshavn # # From Jürgen Appel (2022-11-25): # https://ina.gl/samlinger/oversigt-over-samlinger/samling/dagsordener/dagsorden.aspx?lang=da&day=24-11-2022 -# If I understand this correctly, from the next planned switch to -# summer time, Greenland will permanently stay at that time, i.e. no -# switch back to winter time in 2023 will occur. -# -# From Paul Eggert (2022-11-28): -# The official document in Danish -# https://naalakkersuisut.gl/-/media/naalakkersuisut/filer/kundgoerelser/2022/11/2511/31_da_inatsisartutlov-om-tidens-bestemmelse.pdf?la=da&hash=A33597D8A38CC7038465241119EF34F3 -# says standard time for Greenland is -02, that Naalakkersuisut can lay down -# rules for DST and can require some areas to use a different time zone, -# and that this all takes effect 2023-03-25 22:00. The abovementioned -# "bekymringer" URL says the intent is no transition March 25, that -# Greenland will not go back to winter time in fall 2023, and that -# only America/Nuuk is affected (though further changes may occur). +# +# From Thomas M. Steenholdt (2022-12-02): +# - The bill to move America/Nuuk from UTC-03 to UTC-02 passed. +# - The bill to stop observing DST did not (Greenland will stop observing DST +# when EU does). +# Details on the implementation are here (section 6): +# https://ina.gl/dvd/EM%202022/pdf/media/2553529/pkt17_em2022_tidens_bestemmelse_bem_da.pdf +# This is how the change will be implemented: +# 1. The shift *to* DST in 2023 happens as normal. +# 2. The shift *from* DST in 2023 happens as normal, but coincides with the +# shift to UTC-02 normaltime (people will not change their clocks here). +# 3. After this, DST is still observed, but as -02/-01 instead of -03/-02. # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Thule 1991 1992 - Mar lastSun 2:00 1:00 D @@ -1172,8 +1169,8 @@ Zone America/Scoresbysund -1:27:52 - LMT 1916 Jul 28 # Ittoqqortoormiit -1:00 EU -01/+00 Zone America/Nuuk -3:26:56 - LMT 1916 Jul 28 # Godthåb -3:00 - -03 1980 Apr 6 2:00 - -3:00 EU -03/-02 2023 Mar 25 22:00 - -2:00 - -02 + -3:00 EU -03/-02 2023 Oct 29 1:00u + -2:00 EU -02/-01 Zone America/Thule -4:35:08 - LMT 1916 Jul 28 # Pituffik -4:00 Thule A%sT @@ -1509,9 +1506,9 @@ Zone Europe/Paris 0:09:21 - LMT 1891 Mar 16 Rule Germany 1946 only - Apr 14 2:00s 1:00 S Rule Germany 1946 only - Oct 7 2:00s 0 - Rule Germany 1947 1949 - Oct Sun>=1 2:00s 0 - -# http://www.ptb.de/de/org/4/44/441/salt.htm says the following transition -# occurred at 3:00 MEZ, not the 2:00 MEZ given in Shanks & Pottenger. -# Go with the PTB. +# https://www.ptb.de/cms/en/ptb/fachabteilungen/abt4/fb-44/ag-441/realisation-of-legal-time-in-germany/dst-and-midsummer-dst-in-germany-until-1979.html +# says the following transition occurred at 3:00 MEZ, not the 2:00 MEZ +# given in Shanks & Pottenger. Go with the PTB. Rule Germany 1947 only - Apr 6 3:00s 1:00 S Rule Germany 1947 only - May 11 2:00s 2:00 M Rule Germany 1947 only - Jun 29 3:00 1:00 S @@ -2272,7 +2269,7 @@ Zone Europe/Bucharest 1:44:24 - LMT 1891 Oct # the State Duma has approved ... the draft bill on returning to # winter time standard and return Russia 11 time zones. The new # regulations will come into effect on October 26, 2014 at 02:00 ... -# http://asozd2.duma.gov.ru/main.nsf/%28Spravka%29?OpenAgent&RN=431985-6&02 +# http://asozd2.duma.gov.ru/main.nsf/(Spravka)?OpenAgent&RN=431985-6&02 # Here is a link where we put together table (based on approved Bill N # 431985-6) with proposed 11 Russian time zones and corresponding # areas/cities/administrative centers in the Russian Federation (in English): @@ -2682,13 +2679,13 @@ Zone Europe/Volgograd 2:57:40 - LMT 1920 Jan 3 3:00 - +03 1930 Jun 21 4:00 - +04 1961 Nov 11 4:00 Russia +04/+05 1988 Mar 27 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s + 3:00 Russia MSK/MSD 1991 Mar 31 2:00s 4:00 - +04 1992 Mar 29 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 2014 Oct 26 2:00s - 3:00 - +03 2018 Oct 28 2:00s + 3:00 Russia MSK/MSD 2011 Mar 27 2:00s + 4:00 - MSK 2014 Oct 26 2:00s + 3:00 - MSK 2018 Oct 28 2:00s 4:00 - +04 2020 Dec 27 2:00s - 3:00 - +03 + 3:00 - MSK # From Paul Eggert (2016-11-11): # Europe/Saratov covers: @@ -2719,11 +2716,11 @@ Zone Europe/Saratov 3:04:18 - LMT 1919 Jul 1 0:00u Zone Europe/Kirov 3:18:48 - LMT 1919 Jul 1 0:00u 3:00 - +03 1930 Jun 21 4:00 Russia +04/+05 1989 Mar 26 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s + 3:00 Russia MSK/MSD 1991 Mar 31 2:00s 4:00 - +04 1992 Mar 29 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 2014 Oct 26 2:00s - 3:00 - +03 + 3:00 Russia MSK/MSD 2011 Mar 27 2:00s + 4:00 - MSK 2014 Oct 26 2:00s + 3:00 - MSK # From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): # Europe/Samara covers... diff --git a/make/data/tzdata/iso3166.tab b/make/data/tzdata/iso3166.tab index fbfb74bec45..cea17732dd1 100644 --- a/make/data/tzdata/iso3166.tab +++ b/make/data/tzdata/iso3166.tab @@ -261,7 +261,7 @@ SY Syria SZ Eswatini (Swaziland) TC Turks & Caicos Is TD Chad -TF French Southern Territories +TF French S. Terr. TG Togo TH Thailand TJ Tajikistan diff --git a/make/data/tzdata/leapseconds b/make/data/tzdata/leapseconds index d6fb840f512..89ce8b89cd2 100644 --- a/make/data/tzdata/leapseconds +++ b/make/data/tzdata/leapseconds @@ -95,11 +95,11 @@ Leap 2016 Dec 31 23:59:60 + S # Any additional leap seconds will come after this. # This Expires line is commented out for now, # so that pre-2020a zic implementations do not reject this file. -#Expires 2023 Jun 28 00:00:00 +#Expires 2023 Dec 28 00:00:00 # POSIX timestamps for the data in this file: #updated 1467936000 (2016-07-08 00:00:00 UTC) -#expires 1687910400 (2023-06-28 00:00:00 UTC) +#expires 1703721600 (2023-12-28 00:00:00 UTC) -# Updated through IERS Bulletin C64 -# File expires on: 28 June 2023 +# Updated through IERS Bulletin C65 +# File expires on: 28 December 2023 diff --git a/make/data/tzdata/northamerica b/make/data/tzdata/northamerica index a5fd701f88c..e240cf35103 100644 --- a/make/data/tzdata/northamerica +++ b/make/data/tzdata/northamerica @@ -1,4 +1,3 @@ -# # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -299,9 +298,10 @@ Zone PST8PDT -8:00 US P%sT # -10 Standard Alaska Time (AST) Alaska-Hawaii standard time (AHST) # -11 (unofficial) Nome (NST) Bering standard time (BST) # -# From Paul Eggert (2000-01-08), following a heads-up from Rives McDow: -# Public law 106-564 (2000-12-23) introduced ... "Chamorro Standard Time" +# From Paul Eggert (2023-01-23), from a 2001-01-08 heads-up from Rives McDow: +# Public law 106-564 (2000-12-23) introduced "Chamorro standard time" # for time in Guam and the Northern Marianas. See the file "australasia". +# Also see 15 U.S.C. §263 . # # From Paul Eggert (2015-04-17): # HST and HDT are standardized abbreviations for Hawaii-Aleutian @@ -618,7 +618,7 @@ Zone America/Los_Angeles -7:52:58 - LMT 1883 Nov 18 20:00u # local times of other Alaskan locations so that they change simultaneously. # From Paul Eggert (2014-07-18): -# One opinion of the early-1980s turmoil in Alaska over time zones and +# One opinion of the early 1980s turmoil in Alaska over time zones and # daylight saving time appeared as graffiti on a Juneau airport wall: # "Welcome to Juneau. Please turn your watch back to the 19th century." # See: Turner W. Alaska's four time zones now two. NY Times 1983-11-01. @@ -690,6 +690,10 @@ Zone America/Los_Angeles -7:52:58 - LMT 1883 Nov 18 20:00u # So they won't be waiting for Alaska to join them on 2019-03-10, but will # rather change their clocks twice in seven weeks. +# From Paul Eggert (2023-01-23): +# America/Adak is for the Aleutian Islands that are part of Alaska +# and are west of 169.5° W. + # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/Juneau 15:02:19 - LMT 1867 Oct 19 15:33:32 -8:57:41 - LMT 1900 Aug 20 12:00 @@ -2148,10 +2152,6 @@ Zone America/Fort_Nelson -8:10:47 - LMT 1884 # Nunavut ... moved ... to incorporate the whole territory into one time zone. # Nunavut moves to single time zone Oct. 31 # http://www.nunatsiaq.com/nunavut/nvt90903_13.html -# -# From Antoine Leca (1999-09-06): -# We then need to create a new timezone for the Kitikmeot region of Nunavut -# to differentiate it from the Yellowknife region. # From Paul Eggert (1999-09-20): # Basic Facts: The New Territory @@ -2345,9 +2345,6 @@ Zone America/Cambridge_Bay 0 - -00 1920 # trading post est.? -5:00 - EST 2000 Nov 5 0:00 -6:00 - CST 2001 Apr 1 3:00 -7:00 Canada M%sT -Zone America/Yellowknife 0 - -00 1935 # Yellowknife founded? - -7:00 NT_YK M%sT 1980 - -7:00 Canada M%sT Zone America/Inuvik 0 - -00 1953 # Inuvik founded -8:00 NT_YK P%sT 1979 Apr lastSun 2:00 -7:00 NT_YK M%sT 1980 @@ -2584,7 +2581,7 @@ Zone America/Dawson -9:17:40 - LMT 1900 Aug 20 # and in addition changes all of Chihuahua to -06 with no DST. # From Heitor David Pinto (2022-11-28): -# Now the northern municipalities want to have the same time zone as the +# Now the northern [municipios] want to have the same time zone as the # respective neighboring cities in the US, for example Juárez in UTC-7 with # DST, matching El Paso, and Ojinaga in UTC-6 with DST, matching Presidio.... # the president authorized the publication of the decree for November 29, @@ -2621,7 +2618,7 @@ Zone America/Merida -5:58:28 - LMT 1922 Jan 1 6:00u -5:00 - EST 1982 Dec 2 -6:00 Mexico C%sT # Coahuila, Nuevo León, Tamaulipas (near US border) -# This includes the following municipalities: +# This includes the following municipios: # in Coahuila: Acuña, Allende, Guerrero, Hidalgo, Jiménez, Morelos, Nava, # Ocampo, Piedras Negras, Villa Unión, Zaragoza # in Nuevo León: Anáhuac @@ -2647,8 +2644,8 @@ Zone America/Mexico_City -6:36:36 - LMT 1922 Jan 1 7:00u -6:00 - CST 2002 Feb 20 -6:00 Mexico C%sT # Chihuahua (near US border - western side) -# This includes the municipalities of Janos, Ascensión, Juárez, Guadalupe, -# and Práxedis G Guerrero. +# This includes the municipios of Janos, Ascensión, Juárez, Guadalupe, and +# Práxedis G Guerrero. # http://gaceta.diputados.gob.mx/PDF/65/2a022/nov/20221124-VII.pdf Zone America/Ciudad_Juarez -7:05:56 - LMT 1922 Jan 1 7:00u -7:00 - MST 1927 Jun 10 23:00 @@ -2662,7 +2659,8 @@ Zone America/Ciudad_Juarez -7:05:56 - LMT 1922 Jan 1 7:00u -6:00 - CST 2022 Nov 30 0:00 -7:00 US M%sT # Chihuahua (near US border - eastern side) -# The municipalities of Coyame del Sotol, Ojinaga, and Manuel Benavides. +# This includes the municipios of Coyame del Sotol, Ojinaga, and Manuel +# Benavides. # http://gaceta.diputados.gob.mx/PDF/65/2a022/nov/20221124-VII.pdf Zone America/Ojinaga -6:57:40 - LMT 1922 Jan 1 7:00u -7:00 - MST 1927 Jun 10 23:00 @@ -3083,7 +3081,7 @@ Zone America/Costa_Rica -5:36:13 - LMT 1890 # San José # # He supplied these references: # -# http://www.prensalatina.com.mx/article.asp?ID=%7B4CC32C1B-A9F7-42FB-8A07-8631AFC923AF%7D&language=ES +# http://www.prensalatina.com.mx/article.asp?ID={4CC32C1B-A9F7-42FB-8A07-8631AFC923AF}&language=ES # http://actualidad.terra.es/sociedad/articulo/cuba_llama_ahorrar_energia_cambio_1957044.htm # # From Alex Krivenyshev (2007-10-25): diff --git a/make/data/tzdata/southamerica b/make/data/tzdata/southamerica index 81fdd793df4..4024e7180cd 100644 --- a/make/data/tzdata/southamerica +++ b/make/data/tzdata/southamerica @@ -231,7 +231,7 @@ Rule Arg 2008 only - Oct Sun>=15 0:00 1:00 - # Hora de verano para la República Argentina # http://buenasiembra.com.ar/esoterismo/astrologia/hora-de-verano-de-la-republica-argentina-27.html # says that standard time in Argentina from 1894-10-31 -# to 1920-05-01 was -4:16:48.25. Go with this more-precise value +# to 1920-05-01 was -4:16:48.25. Go with this more precise value # over Shanks & Pottenger. It is upward compatible with Milne, who # says Córdoba time was -4:16:48.2. diff --git a/make/data/tzdata/zone.tab b/make/data/tzdata/zone.tab index 939432d3456..3edb0d61c80 100644 --- a/make/data/tzdata/zone.tab +++ b/make/data/tzdata/zone.tab @@ -144,9 +144,8 @@ CA +744144-0944945 America/Resolute Central - NU (Resolute) CA +624900-0920459 America/Rankin_Inlet Central - NU (central) CA +5024-10439 America/Regina CST - SK (most areas) CA +5017-10750 America/Swift_Current CST - SK (midwest) -CA +5333-11328 America/Edmonton Mountain - AB; BC (E); SK (W) +CA +5333-11328 America/Edmonton Mountain - AB; BC (E); NT (E); SK (W) CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) -CA +6227-11421 America/Yellowknife Mountain - NT (central) CA +682059-1334300 America/Inuvik Mountain - NT (west) CA +4906-11631 America/Creston MST - BC (Creston) CA +5546-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) @@ -162,7 +161,7 @@ CG -0416+01517 Africa/Brazzaville CH +4723+00832 Europe/Zurich CI +0519-00402 Africa/Abidjan CK -2114-15946 Pacific/Rarotonga -CL -3327-07040 America/Santiago Chile (most areas) +CL -3327-07040 America/Santiago most of Chile CL -5309-07055 America/Punta_Arenas Region of Magallanes CL -2709-10926 Pacific/Easter Easter Island CM +0403+00942 Africa/Douala @@ -174,10 +173,10 @@ CU +2308-08222 America/Havana CV +1455-02331 Atlantic/Cape_Verde CW +1211-06900 America/Curacao CX -1025+10543 Indian/Christmas -CY +3510+03322 Asia/Nicosia Cyprus (most areas) +CY +3510+03322 Asia/Nicosia most of Cyprus CY +3507+03357 Asia/Famagusta Northern Cyprus CZ +5005+01426 Europe/Prague -DE +5230+01322 Europe/Berlin Germany (most areas) +DE +5230+01322 Europe/Berlin most of Germany DE +4742+00841 Europe/Busingen Busingen DJ +1136+04309 Africa/Djibouti DK +5540+01235 Europe/Copenhagen @@ -210,7 +209,7 @@ GF +0456-05220 America/Cayenne GG +492717-0023210 Europe/Guernsey GH +0533-00013 Africa/Accra GI +3608-00521 Europe/Gibraltar -GL +6411-05144 America/Nuuk Greenland (most areas) +GL +6411-05144 America/Nuuk most of Greenland GL +7646-01840 America/Danmarkshavn National Park (east coast) GL +7029-02158 America/Scoresbysund Scoresbysund/Ittoqqortoormiit GL +7634-06847 America/Thule Thule/Pituffik @@ -258,7 +257,7 @@ KP +3901+12545 Asia/Pyongyang KR +3733+12658 Asia/Seoul KW +2920+04759 Asia/Kuwait KY +1918-08123 America/Cayman -KZ +4315+07657 Asia/Almaty Kazakhstan (most areas) +KZ +4315+07657 Asia/Almaty most of Kazakhstan KZ +4448+06528 Asia/Qyzylorda Qyzylorda/Kyzylorda/Kzyl-Orda KZ +5312+06337 Asia/Qostanay Qostanay/Kostanay/Kustanay KZ +5017+05710 Asia/Aqtobe Aqtobe/Aktobe @@ -282,12 +281,12 @@ MD +4700+02850 Europe/Chisinau ME +4226+01916 Europe/Podgorica MF +1804-06305 America/Marigot MG -1855+04731 Indian/Antananarivo -MH +0709+17112 Pacific/Majuro Marshall Islands (most areas) +MH +0709+17112 Pacific/Majuro most of Marshall Islands MH +0905+16720 Pacific/Kwajalein Kwajalein MK +4159+02126 Europe/Skopje ML +1239-00800 Africa/Bamako MM +1647+09610 Asia/Yangon -MN +4755+10653 Asia/Ulaanbaatar Mongolia (most areas) +MN +4755+10653 Asia/Ulaanbaatar most of Mongolia MN +4801+09139 Asia/Hovd Bayan-Olgiy, Govi-Altai, Hovd, Uvs, Zavkhan MN +4804+11430 Asia/Choibalsan Dornod, Sukhbaatar MO +221150+1133230 Asia/Macau @@ -325,7 +324,7 @@ NO +5955+01045 Europe/Oslo NP +2743+08519 Asia/Kathmandu NR -0031+16655 Pacific/Nauru NU -1901-16955 Pacific/Niue -NZ -3652+17446 Pacific/Auckland New Zealand (most areas) +NZ -3652+17446 Pacific/Auckland most of New Zealand NZ -4357-17633 Pacific/Chatham Chatham Islands OM +2336+05835 Asia/Muscat PA +0858-07932 America/Panama @@ -333,7 +332,7 @@ PE -1203-07703 America/Lima PF -1732-14934 Pacific/Tahiti Society Islands PF -0900-13930 Pacific/Marquesas Marquesas Islands PF -2308-13457 Pacific/Gambier Gambier Islands -PG -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas) +PG -0930+14710 Pacific/Port_Moresby most of Papua New Guinea PG -0613+15534 Pacific/Bougainville Bougainville PH +1435+12100 Asia/Manila PK +2452+06703 Asia/Karachi @@ -379,7 +378,7 @@ RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky RU +5934+15048 Asia/Magadan MSK+08 - Magadan RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island -RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); North Kuril Is +RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); N Kuril Is RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea RW -0157+03004 Africa/Kigali @@ -420,7 +419,7 @@ TT +1039-06131 America/Port_of_Spain TV -0831+17913 Pacific/Funafuti TW +2503+12130 Asia/Taipei TZ -0648+03917 Africa/Dar_es_Salaam -UA +5026+03031 Europe/Kyiv Ukraine (most areas) +UA +5026+03031 Europe/Kyiv most of Ukraine UG +0019+03225 Africa/Kampala UM +2813-17722 Pacific/Midway Midway Islands UM +1917+16637 Pacific/Wake Wake Island @@ -443,7 +442,7 @@ US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) US +394421-1045903 America/Denver Mountain (most areas) US +433649-1161209 America/Boise Mountain - ID (south); OR (east) -US +332654-1120424 America/Phoenix MST - Arizona (except Navajo) +US +332654-1120424 America/Phoenix MST - AZ (except Navajo) US +340308-1181434 America/Los_Angeles Pacific US +611305-1495401 America/Anchorage Alaska (most areas) US +581807-1342511 America/Juneau Alaska - Juneau area @@ -451,7 +450,7 @@ US +571035-1351807 America/Sitka Alaska - Sitka area US +550737-1313435 America/Metlakatla Alaska - Annette Island US +593249-1394338 America/Yakutat Alaska - Yakutat US +643004-1652423 America/Nome Alaska (west) -US +515248-1763929 America/Adak Aleutian Islands +US +515248-1763929 America/Adak Alaska - western Aleutians US +211825-1575130 Pacific/Honolulu Hawaii UY -345433-0561245 America/Montevideo UZ +3940+06648 Asia/Samarkand Uzbekistan (west) diff --git a/make/devkit/Tools.gmk b/make/devkit/Tools.gmk index 19eccf89be2..e94a74d0063 100644 --- a/make/devkit/Tools.gmk +++ b/make/devkit/Tools.gmk @@ -87,8 +87,17 @@ endif # Define external dependencies # Latest that could be made to work. -GCC_VER := 10.3.0 -ifeq ($(GCC_VER), 10.3.0) +GCC_VER := 11.2.0 +ifeq ($(GCC_VER), 11.2.0) + gcc_ver := gcc-11.2.0 + binutils_ver := binutils-2.37 + ccache_ver := ccache-3.7.12 + mpfr_ver := mpfr-4.1.0 + gmp_ver := gmp-6.2.1 + mpc_ver := mpc-1.2.1 + gdb_ver := gdb-11.1 + REQUIRED_MIN_MAKE_MAJOR_VERSION := 4 +else ifeq ($(GCC_VER), 10.3.0) gcc_ver := gcc-10.3.0 binutils_ver := binutils-2.36.1 ccache_ver := ccache-3.7.11 diff --git a/make/hotspot/lib/CompileGtest.gmk b/make/hotspot/lib/CompileGtest.gmk index cb2bbccc168..f16b9a747bc 100644 --- a/make/hotspot/lib/CompileGtest.gmk +++ b/make/hotspot/lib/CompileGtest.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -49,7 +49,7 @@ $(eval $(call SetupJdkLibrary, BUILD_GTEST_LIBGTEST, \ $(GTEST_FRAMEWORK_SRC)/googletest/src \ $(GTEST_FRAMEWORK_SRC)/googlemock/src, \ INCLUDE_FILES := gtest-all.cc gmock-all.cc, \ - DISABLED_WARNINGS_gcc := undef unused-result format-nonliteral, \ + DISABLED_WARNINGS_gcc := undef unused-result format-nonliteral maybe-uninitialized, \ DISABLED_WARNINGS_clang := undef unused-result format-nonliteral, \ CFLAGS := $(JVM_CFLAGS) \ -I$(GTEST_FRAMEWORK_SRC)/googletest \ diff --git a/make/jdk/src/classes/build/tools/charsetmapping/SPI.java b/make/jdk/src/classes/build/tools/charsetmapping/SPI.java index 3ed09fd938b..15e0b9fcf68 100644 --- a/make/jdk/src/classes/build/tools/charsetmapping/SPI.java +++ b/make/jdk/src/classes/build/tools/charsetmapping/SPI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,18 +50,19 @@ public static void genClass(String type, out.println(line); } else { charsets.values() - .stream() - .filter(cs -> cs.pkgName.equals("sun.nio.cs.ext") && - !cs.isInternal && - (cs.os == null || cs.os.equals(os))) - .forEach( cs -> { - out.printf(" charset(\"%s\", \"%s\",%n", cs.csName, cs.clzName); - out.printf(" new String[] {%n"); - for (String alias : cs.aliases) { - out.printf(" \"%s\",%n", alias); - } - out.printf(" });%n%n"); - }); + .stream() + .filter(cs -> cs.pkgName.equals("sun.nio.cs.ext") && + !cs.isInternal && + (cs.os == null || cs.os.equals(os))) + .forEach( cs -> { + out.printf(" charset(\"%s\", \"%s\",%n", cs.csName, cs.clzName); + out.printf(" new String[] {%n"); + for (String alias : cs.aliases) { + out.printf(" \"%s\",%n", + alias); + } + out.printf(" });%n%n"); + }); } } } else if (type.startsWith("stdcs")) { // StandardCharsets.java @@ -93,8 +94,15 @@ public static void genClass(String type, .filter(cs -> cs.pkgName.equals("sun.nio.cs")) .forEach( cs -> { if (cs.aliases == null || cs.aliases.length == 0) { - out.printf(" static String[] aliases_%s() { return null; }%n%n", - cs.clzName); + if (cs.csName.equals("GB18030")) { + out.printf(" static String[] aliases_GB18030() { return new String[] {%n"); + out.printf(" GB18030.IS_2000 ? \"gb18030-2000\" : \"gb18030-2022\"%n"); + out.printf(" };%n"); + out.printf(" }%n%n"); + } else { + out.printf(" static String[] aliases_%s() { return null; }%n%n", + cs.clzName); + } } else { boolean methodEnd = true; // non-final for SJIS and MS932 to support sun.nio.cs.map diff --git a/make/jdk/src/classes/build/tools/cldrconverter/Bundle.java b/make/jdk/src/classes/build/tools/cldrconverter/Bundle.java index 9f262fa0106..e7874e6e3b6 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/Bundle.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/Bundle.java @@ -197,9 +197,7 @@ Map getTargetMap() throws Exception { // parentsMap contains resources from id's parents. Map parentsMap = new HashMap<>(); for (int i = cldrBundles.length - 1; i > index; i--) { - if (!("no".equals(cldrBundles[i]) || cldrBundles[i].startsWith("no_"))) { - parentsMap.putAll(CLDRConverter.getCLDRBundle(cldrBundles[i])); - } + parentsMap.putAll(CLDRConverter.getCLDRBundle(cldrBundles[i])); } // Duplicate myMap as parentsMap for "root" so that the // fallback works. This is a hack, though. diff --git a/make/modules/java.base/lib/CoreLibraries.gmk b/make/modules/java.base/lib/CoreLibraries.gmk index e7188218df3..bb09d8cf8a2 100644 --- a/make/modules/java.base/lib/CoreLibraries.gmk +++ b/make/modules/java.base/lib/CoreLibraries.gmk @@ -49,6 +49,7 @@ $(eval $(call SetupNativeCompilation, BUILD_LIBFDLIBM, \ CFLAGS_windows_debug := -DLOGGING, \ CFLAGS_aix := -qfloat=nomaf, \ DISABLED_WARNINGS_gcc := sign-compare misleading-indentation array-bounds, \ + DISABLED_WARNINGS_gcc_k_rem_pio2.c := maybe-uninitialized, \ DISABLED_WARNINGS_clang := sign-compare misleading-indentation, \ DISABLED_WARNINGS_microsoft := 4146 4244 4018, \ ARFLAGS := $(ARFLAGS), \ diff --git a/make/modules/java.desktop/lib/Awt2dLibraries.gmk b/make/modules/java.desktop/lib/Awt2dLibraries.gmk index 918a5884851..9fd01cabf0e 100644 --- a/make/modules/java.desktop/lib/Awt2dLibraries.gmk +++ b/make/modules/java.desktop/lib/Awt2dLibraries.gmk @@ -446,7 +446,7 @@ else -DHB_NO_PRAGMA_GCC_DIAGNOSTIC endif ifeq ($(call isTargetOs, linux macosx), true) - HARFBUZZ_CFLAGS += -DHAVE_INTEL_ATOMIC_PRIMITIVES + HARFBUZZ_CFLAGS += -DHAVE_INTEL_ATOMIC_PRIMITIVES -DHB_NO_VISIBILITY endif # Early re-canonizing has to be disabled to workaround an internal XlC compiler error @@ -459,8 +459,9 @@ else LIBFONTMANAGER_EXCLUDE_FILES += libharfbuzz/hb-ft.cc HARFBUZZ_DISABLED_WARNINGS_gcc := type-limits missing-field-initializers strict-aliasing + # noexcept-type required for GCC 7 builds. Not required for GCC 8+. HARFBUZZ_DISABLED_WARNINGS_CXX_gcc := reorder delete-non-virtual-dtor strict-overflow \ - maybe-uninitialized class-memaccess unused-result extra use-after-free + maybe-uninitialized class-memaccess unused-result extra use-after-free noexcept-type HARFBUZZ_DISABLED_WARNINGS_clang := unused-value incompatible-pointer-types \ tautological-constant-out-of-range-compare int-to-pointer-cast \ undef missing-field-initializers range-loop-analysis \ diff --git a/make/modules/jdk.internal.vm.ci/Java.gmk b/make/modules/jdk.internal.vm.ci/Java.gmk index ad4991e5414..8720c97a36b 100644 --- a/make/modules/jdk.internal.vm.ci/Java.gmk +++ b/make/modules/jdk.internal.vm.ci/Java.gmk @@ -26,13 +26,5 @@ # -parameters provides method's parameters information in class file, # JVMCI compilers make use of that information for various sanity checks. # Don't use Indy strings concatenation to have good JVMCI startup performance. -# The exports are needed since JVMCI is dynamically exported (see -# jdk.vm.ci.services.internal.ReflectionAccessJDK::openJVMCITo). JAVAC_FLAGS += -parameters -XDstringConcat=inline - -## WORKAROUND jdk.internal.vm.ci source structure issue -JVMCI_MODULESOURCEPATH := $(MODULESOURCEPATH) \ - $(subst /$(MODULE)/,/*/, $(filter-out %processor/src, \ - $(wildcard $(TOPDIR)/src/$(MODULE)/share/classes/*/src))) -MODULESOURCEPATH := $(call PathList, $(JVMCI_MODULESOURCEPATH)) diff --git a/make/test/JtregNativeHotspot.gmk b/make/test/JtregNativeHotspot.gmk index 58390110251..a55cce7ab08 100644 --- a/make/test/JtregNativeHotspot.gmk +++ b/make/test/JtregNativeHotspot.gmk @@ -871,7 +871,7 @@ BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exesigtest := -ljvm ifeq ($(call isTargetOs, windows), true) BUILD_HOTSPOT_JTREG_EXECUTABLES_CFLAGS_exeFPRegs := -MT - BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c + BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c libnativeStack.c BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit := jvm.lib else BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libbootclssearch_agent += -lpthread @@ -1508,6 +1508,7 @@ else BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libgetphase002 += -lpthread BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libterminatedThread += -lpthread BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit += -ljvm + BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libnativeStack += -lpthread endif # This evaluation is expensive and should only be done if this target was diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 91ea50c002e..b9f772c9642 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -8646,6 +8646,7 @@ instruct membar_release() %{ instruct membar_storestore() %{ match(MemBarStoreStore); + match(StoreStoreFence); ins_cost(VOLATILE_REF_COST); format %{ "MEMBAR-store-store" %} @@ -17028,16 +17029,17 @@ instruct string_compress(iRegP_R2 src, iRegP_R1 dst, iRegI_R3 len, iRegI_R0 result, rFlagsReg cr) %{ match(Set result (StrCompressedCopy src (Binary dst len))); - effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL cr); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, + USE_KILL src, USE_KILL dst, USE len, KILL cr); - format %{ "String Compress $src,$dst -> $result // KILL R1, R2, R3, R4" %} + format %{ "String Compress $src,$dst,$len -> $result // KILL $src,$dst" %} ins_encode %{ __ char_array_compress($src$$Register, $dst$$Register, $len$$Register, + $result$$Register, $tmp1$$FloatRegister, $tmp2$$FloatRegister, - $tmp3$$FloatRegister, $tmp4$$FloatRegister, - $result$$Register); + $tmp3$$FloatRegister, $tmp4$$FloatRegister); %} - ins_pipe( pipe_slow ); + ins_pipe(pipe_slow); %} // fast byte[] to char[] inflation @@ -17062,22 +17064,43 @@ instruct string_inflate(Universe dummy, iRegP_R0 src, iRegP_R1 dst, iRegI_R2 len // encode char[] to byte[] in ISO_8859_1 instruct encode_iso_array(iRegP_R2 src, iRegP_R1 dst, iRegI_R3 len, - vRegD_V0 Vtmp1, vRegD_V1 Vtmp2, - vRegD_V2 Vtmp3, vRegD_V3 Vtmp4, + vRegD_V0 vtmp0, vRegD_V1 vtmp1, + vRegD_V2 vtmp2, vRegD_V3 vtmp3, iRegI_R0 result, rFlagsReg cr) %{ predicate(!((EncodeISOArrayNode*)n)->is_ascii()); match(Set result (EncodeISOArray src (Binary dst len))); - effect(USE_KILL src, USE_KILL dst, USE_KILL len, - KILL Vtmp1, KILL Vtmp2, KILL Vtmp3, KILL Vtmp4, KILL cr); + effect(USE_KILL src, USE_KILL dst, USE len, + KILL vtmp0, KILL vtmp1, KILL vtmp2, KILL vtmp3, KILL cr); - format %{ "Encode array $src,$dst,$len -> $result" %} + format %{ "Encode ISO array $src,$dst,$len -> $result" %} ins_encode %{ __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, - $result$$Register, $Vtmp1$$FloatRegister, $Vtmp2$$FloatRegister, - $Vtmp3$$FloatRegister, $Vtmp4$$FloatRegister); + $result$$Register, false, + $vtmp0$$FloatRegister, $vtmp1$$FloatRegister, + $vtmp2$$FloatRegister, $vtmp3$$FloatRegister); %} - ins_pipe( pipe_class_memory ); + ins_pipe(pipe_class_memory); +%} + +instruct encode_ascii_array(iRegP_R2 src, iRegP_R1 dst, iRegI_R3 len, + vRegD_V0 vtmp0, vRegD_V1 vtmp1, + vRegD_V2 vtmp2, vRegD_V3 vtmp3, + iRegI_R0 result, rFlagsReg cr) +%{ + predicate(((EncodeISOArrayNode*)n)->is_ascii()); + match(Set result (EncodeISOArray src (Binary dst len))); + effect(USE_KILL src, USE_KILL dst, USE len, + KILL vtmp0, KILL vtmp1, KILL vtmp2, KILL vtmp3, KILL cr); + + format %{ "Encode ASCII array $src,$dst,$len -> $result" %} + ins_encode %{ + __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, + $result$$Register, true, + $vtmp0$$FloatRegister, $vtmp1$$FloatRegister, + $vtmp2$$FloatRegister, $vtmp3$$FloatRegister); + %} + ins_pipe(pipe_class_memory); %} // ============================================================================ diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index 70f50dbe4ae..1fea866cc1d 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -2448,6 +2448,12 @@ void mvnw(Register Rd, Register Rm, INSN(cnt, 0, 0b100000010110, 0); // accepted arrangements: T8B, T16B INSN(uaddlp, 1, 0b100000001010, 2); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S INSN(uaddlv, 1, 0b110000001110, 1); // accepted arrangements: T8B, T16B, T4H, T8H, T4S + // Zero compare. + INSN(cmeq, 0, 0b100000100110, 3); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S, T2D + INSN(cmge, 1, 0b100000100010, 3); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S, T2D + INSN(cmgt, 0, 0b100000100010, 3); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S, T2D + INSN(cmle, 1, 0b100000100110, 3); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S, T2D + INSN(cmlt, 0, 0b100000101010, 3); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S, T2D #undef INSN diff --git a/src/hotspot/cpu/aarch64/atomic_aarch64.hpp b/src/hotspot/cpu/aarch64/atomic_aarch64.hpp index 6f9425e43ac..f1e1f04c244 100644 --- a/src/hotspot/cpu/aarch64/atomic_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/atomic_aarch64.hpp @@ -37,6 +37,8 @@ typedef uint64_t (*aarch64_atomic_stub_t)(volatile void *ptr, uint64_t arg1, uin // Pointers to stubs extern aarch64_atomic_stub_t aarch64_atomic_fetch_add_4_impl; extern aarch64_atomic_stub_t aarch64_atomic_fetch_add_8_impl; +extern aarch64_atomic_stub_t aarch64_atomic_fetch_add_4_relaxed_impl; +extern aarch64_atomic_stub_t aarch64_atomic_fetch_add_8_relaxed_impl; extern aarch64_atomic_stub_t aarch64_atomic_xchg_4_impl; extern aarch64_atomic_stub_t aarch64_atomic_xchg_8_impl; extern aarch64_atomic_stub_t aarch64_atomic_cmpxchg_1_impl; diff --git a/src/hotspot/cpu/aarch64/jvmciCodeInstaller_aarch64.cpp b/src/hotspot/cpu/aarch64/jvmciCodeInstaller_aarch64.cpp index 17b9780129d..c970070c45a 100644 --- a/src/hotspot/cpu/aarch64/jvmciCodeInstaller_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/jvmciCodeInstaller_aarch64.cpp @@ -138,21 +138,21 @@ void CodeInstaller::pd_relocate_JavaMethod(CodeBuffer &cbuf, JVMCIObject hotspot assert(method == NULL || !method->is_static(), "cannot call static method with invokeinterface"); NativeCall* call = nativeCall_at(_instructions->start() + pc_offset); _instructions->relocate(call->instruction_address(), virtual_call_Relocation::spec(_invoke_mark_pc)); - call->trampoline_jump(cbuf, SharedRuntime::get_resolve_virtual_call_stub()); + call->trampoline_jump(cbuf, SharedRuntime::get_resolve_virtual_call_stub(), JVMCI_CHECK); break; } case INVOKESTATIC: { assert(method == NULL || method->is_static(), "cannot call non-static method with invokestatic"); NativeCall* call = nativeCall_at(_instructions->start() + pc_offset); _instructions->relocate(call->instruction_address(), relocInfo::static_call_type); - call->trampoline_jump(cbuf, SharedRuntime::get_resolve_static_call_stub()); + call->trampoline_jump(cbuf, SharedRuntime::get_resolve_static_call_stub(), JVMCI_CHECK); break; } case INVOKESPECIAL: { assert(method == NULL || !method->is_static(), "cannot call static method with invokespecial"); NativeCall* call = nativeCall_at(_instructions->start() + pc_offset); _instructions->relocate(call->instruction_address(), relocInfo::opt_virtual_call_type); - call->trampoline_jump(cbuf, SharedRuntime::get_resolve_opt_virtual_call_stub()); + call->trampoline_jump(cbuf, SharedRuntime::get_resolve_opt_virtual_call_stub(), JVMCI_CHECK); break; } default: diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 676a548d039..8d819fd6a88 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -5031,111 +5031,118 @@ void MacroAssembler::fill_words(Register base, Register cnt, Register value) bind(fini); } -// Intrinsic for sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray and -// java/lang/StringUTF16.compress. +// Intrinsic for +// +// - sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray +// return the number of characters copied. +// - java/lang/StringUTF16.compress +// return zero (0) if copy fails, otherwise 'len'. +// +// This version always returns the number of characters copied, and does not +// clobber the 'len' register. A successful copy will complete with the post- +// condition: 'res' == 'len', while an unsuccessful copy will exit with the +// post-condition: 0 <= 'res' < 'len'. +// +// NOTE: Attempts to use 'ld2' (and 'umaxv' in the ISO part) has proven to +// degrade performance (on Ampere Altra - Neoverse N1), to an extent +// beyond the acceptable, even though the footprint would be smaller. +// Using 'umaxv' in the ASCII-case comes with a small penalty but does +// avoid additional bloat. +// void MacroAssembler::encode_iso_array(Register src, Register dst, - Register len, Register result, - FloatRegister Vtmp1, FloatRegister Vtmp2, - FloatRegister Vtmp3, FloatRegister Vtmp4) + Register len, Register res, bool ascii, + FloatRegister vtmp0, FloatRegister vtmp1, + FloatRegister vtmp2, FloatRegister vtmp3) { - Label DONE, SET_RESULT, NEXT_32, NEXT_32_PRFM, LOOP_8, NEXT_8, LOOP_1, NEXT_1, - NEXT_32_START, NEXT_32_PRFM_START; - Register tmp1 = rscratch1, tmp2 = rscratch2; - - mov(result, len); // Save initial len - - cmp(len, (u1)8); // handle shortest strings first - br(LT, LOOP_1); - cmp(len, (u1)32); - br(LT, NEXT_8); - // The following code uses the SIMD 'uzp1' and 'uzp2' instructions - // to convert chars to bytes - if (SoftwarePrefetchHintDistance >= 0) { - ld1(Vtmp1, Vtmp2, Vtmp3, Vtmp4, T8H, src); - subs(tmp2, len, SoftwarePrefetchHintDistance/2 + 16); - br(LE, NEXT_32_START); - b(NEXT_32_PRFM_START); - BIND(NEXT_32_PRFM); - ld1(Vtmp1, Vtmp2, Vtmp3, Vtmp4, T8H, src); - BIND(NEXT_32_PRFM_START); - prfm(Address(src, SoftwarePrefetchHintDistance)); - orr(v4, T16B, Vtmp1, Vtmp2); - orr(v5, T16B, Vtmp3, Vtmp4); - uzp1(Vtmp1, T16B, Vtmp1, Vtmp2); - uzp1(Vtmp3, T16B, Vtmp3, Vtmp4); - uzp2(v5, T16B, v4, v5); // high bytes - umov(tmp2, v5, D, 1); - fmovd(tmp1, v5); - orr(tmp1, tmp1, tmp2); - cbnz(tmp1, LOOP_8); - stpq(Vtmp1, Vtmp3, dst); - sub(len, len, 32); - add(dst, dst, 32); - add(src, src, 64); - subs(tmp2, len, SoftwarePrefetchHintDistance/2 + 16); - br(GE, NEXT_32_PRFM); - cmp(len, (u1)32); - br(LT, LOOP_8); - BIND(NEXT_32); - ld1(Vtmp1, Vtmp2, Vtmp3, Vtmp4, T8H, src); - BIND(NEXT_32_START); - } else { - BIND(NEXT_32); - ld1(Vtmp1, Vtmp2, Vtmp3, Vtmp4, T8H, src); - } - prfm(Address(src, SoftwarePrefetchHintDistance)); - uzp1(v4, T16B, Vtmp1, Vtmp2); - uzp1(v5, T16B, Vtmp3, Vtmp4); - orr(Vtmp1, T16B, Vtmp1, Vtmp2); - orr(Vtmp3, T16B, Vtmp3, Vtmp4); - uzp2(Vtmp1, T16B, Vtmp1, Vtmp3); // high bytes - umov(tmp2, Vtmp1, D, 1); - fmovd(tmp1, Vtmp1); - orr(tmp1, tmp1, tmp2); - cbnz(tmp1, LOOP_8); - stpq(v4, v5, dst); - sub(len, len, 32); - add(dst, dst, 32); - add(src, src, 64); - cmp(len, (u1)32); - br(GE, NEXT_32); - cbz(len, DONE); - - BIND(LOOP_8); - cmp(len, (u1)8); - br(LT, LOOP_1); - BIND(NEXT_8); - ld1(Vtmp1, T8H, src); - uzp1(Vtmp2, T16B, Vtmp1, Vtmp1); // low bytes - uzp2(Vtmp3, T16B, Vtmp1, Vtmp1); // high bytes - fmovd(tmp1, Vtmp3); - cbnz(tmp1, NEXT_1); - strd(Vtmp2, dst); - - sub(len, len, 8); - add(dst, dst, 8); - add(src, src, 16); - cmp(len, (u1)8); - br(GE, NEXT_8); - - BIND(LOOP_1); - - cbz(len, DONE); - BIND(NEXT_1); - ldrh(tmp1, Address(post(src, 2))); - tst(tmp1, 0xff00); - br(NE, SET_RESULT); - strb(tmp1, Address(post(dst, 1))); - subs(len, len, 1); - br(GT, NEXT_1); - - BIND(SET_RESULT); - sub(result, result, len); // Return index where we stopped - // Return len == 0 if we processed all - // characters - BIND(DONE); -} + Register cnt = res; + Register max = rscratch1; + Register chk = rscratch2; + + prfm(Address(src), PLDL1STRM); + movw(cnt, len); + +#define ASCII(insn) do { if (ascii) { insn; } } while (0) + Label LOOP_32, DONE_32, FAIL_32; + + BIND(LOOP_32); + { + cmpw(cnt, 32); + br(LT, DONE_32); + ld1(vtmp0, vtmp1, vtmp2, vtmp3, T8H, Address(post(src, 64))); + // Extract lower bytes. + FloatRegister vlo0 = v4; + FloatRegister vlo1 = v5; + uzp1(vlo0, T16B, vtmp0, vtmp1); + uzp1(vlo1, T16B, vtmp2, vtmp3); + // Merge bits... + orr(vtmp0, T16B, vtmp0, vtmp1); + orr(vtmp2, T16B, vtmp2, vtmp3); + // Extract merged upper bytes. + FloatRegister vhix = vtmp0; + uzp2(vhix, T16B, vtmp0, vtmp2); + // ISO-check on hi-parts (all zero). + // ASCII-check on lo-parts (no sign). + FloatRegister vlox = vtmp1; // Merge lower bytes. + ASCII(orr(vlox, T16B, vlo0, vlo1)); + umov(chk, vhix, D, 1); ASCII(cmlt(vlox, T16B, vlox)); + fmovd(max, vhix); ASCII(umaxv(vlox, T16B, vlox)); + orr(chk, chk, max); ASCII(umov(max, vlox, B, 0)); + ASCII(orr(chk, chk, max)); + cbnz(chk, FAIL_32); + subw(cnt, cnt, 32); + st1(vlo0, vlo1, T16B, Address(post(dst, 32))); + b(LOOP_32); + } + BIND(FAIL_32); + sub(src, src, 64); + BIND(DONE_32); + + Label LOOP_8, SKIP_8; + + BIND(LOOP_8); + { + cmpw(cnt, 8); + br(LT, SKIP_8); + FloatRegister vhi = vtmp0; + FloatRegister vlo = vtmp1; + ld1(vtmp3, T8H, src); + uzp1(vlo, T16B, vtmp3, vtmp3); + uzp2(vhi, T16B, vtmp3, vtmp3); + // ISO-check on hi-parts (all zero). + // ASCII-check on lo-parts (no sign). + ASCII(cmlt(vtmp2, T16B, vlo)); + fmovd(chk, vhi); ASCII(umaxv(vtmp2, T16B, vtmp2)); + ASCII(umov(max, vtmp2, B, 0)); + ASCII(orr(chk, chk, max)); + cbnz(chk, SKIP_8); + + strd(vlo, Address(post(dst, 8))); + subw(cnt, cnt, 8); + add(src, src, 16); + b(LOOP_8); + } + BIND(SKIP_8); + +#undef ASCII + + Label LOOP, DONE; + + cbz(cnt, DONE); + BIND(LOOP); + { + Register chr = rscratch1; + ldrh(chr, Address(post(src, 2))); + tst(chr, ascii ? 0xff80 : 0xff00); + br(NE, DONE); + strb(chr, Address(post(dst, 1))); + subs(cnt, cnt, 1); + br(GT, LOOP); + } + BIND(DONE); + // Return index where we stopped. + subw(res, len, cnt); +} // Inflate byte[] array to char[]. address MacroAssembler::byte_array_inflate(Register src, Register dst, Register len, @@ -5244,13 +5251,13 @@ address MacroAssembler::byte_array_inflate(Register src, Register dst, Register // Compress char[] array to byte[]. void MacroAssembler::char_array_compress(Register src, Register dst, Register len, - FloatRegister tmp1Reg, FloatRegister tmp2Reg, - FloatRegister tmp3Reg, FloatRegister tmp4Reg, - Register result) { - encode_iso_array(src, dst, len, result, - tmp1Reg, tmp2Reg, tmp3Reg, tmp4Reg); - cmp(len, zr); - csel(result, result, zr, EQ); + Register res, + FloatRegister tmp0, FloatRegister tmp1, + FloatRegister tmp2, FloatRegister tmp3) { + encode_iso_array(src, dst, len, res, false, tmp0, tmp1, tmp2, tmp3); + // Adjust result: res == len ? len : 0 + cmp(len, res); + csel(res, res, zr, EQ); } // get_thread() can be called anywhere inside generated code so we diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index e403289e22f..fffd7ac07fd 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -1273,14 +1273,15 @@ class MacroAssembler: public Assembler { FloatRegister vtmp3, Register tmp4); void char_array_compress(Register src, Register dst, Register len, - FloatRegister tmp1Reg, FloatRegister tmp2Reg, - FloatRegister tmp3Reg, FloatRegister tmp4Reg, - Register result); + Register res, + FloatRegister vtmp0, FloatRegister vtmp1, + FloatRegister vtmp2, FloatRegister vtmp3); void encode_iso_array(Register src, Register dst, - Register len, Register result, - FloatRegister Vtmp1, FloatRegister Vtmp2, - FloatRegister Vtmp3, FloatRegister Vtmp4); + Register len, Register res, bool ascii, + FloatRegister vtmp0, FloatRegister vtmp1, + FloatRegister vtmp2, FloatRegister vtmp3); + void fast_log(FloatRegister vtmp0, FloatRegister vtmp1, FloatRegister vtmp2, FloatRegister vtmp3, FloatRegister vtmp4, FloatRegister vtmp5, FloatRegister tmpC1, FloatRegister tmpC2, FloatRegister tmpC3, diff --git a/src/hotspot/cpu/aarch64/matcher_aarch64.hpp b/src/hotspot/cpu/aarch64/matcher_aarch64.hpp index 5f93c841e4e..cd4bb96e4ad 100644 --- a/src/hotspot/cpu/aarch64/matcher_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/matcher_aarch64.hpp @@ -156,6 +156,6 @@ } // Implements a variant of EncodeISOArrayNode that encode ASCII only - static const bool supports_encode_ascii_array = false; + static const bool supports_encode_ascii_array = true; #endif // CPU_AARCH64_MATCHER_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp index d808e4b5b53..d5a5503213c 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp @@ -39,6 +39,9 @@ #ifdef COMPILER1 #include "c1/c1_Runtime1.hpp" #endif +#if INCLUDE_JVMCI +#include "jvmci/jvmciEnv.hpp" +#endif void NativeCall::verify() { assert(NativeCall::is_call_at((address)this), "unexpected code at call site"); @@ -524,23 +527,26 @@ void NativeCallTrampolineStub::set_destination(address new_destination) { OrderAccess::release(); } +#if INCLUDE_JVMCI // Generate a trampoline for a branch to dest. If there's no need for a // trampoline, simply patch the call directly to dest. -address NativeCall::trampoline_jump(CodeBuffer &cbuf, address dest) { +void NativeCall::trampoline_jump(CodeBuffer &cbuf, address dest, JVMCI_TRAPS) { MacroAssembler a(&cbuf); - address stub = NULL; - - if (a.far_branches() - && ! is_NativeCallTrampolineStub_at(instruction_address() + displacement())) { - stub = a.emit_trampoline_stub(instruction_address() - cbuf.insts()->start(), dest); - } - if (stub == NULL) { - // If we generated no stub, patch this call directly to dest. - // This will happen if we don't need far branches or if there - // already was a trampoline. + if (!a.far_branches()) { + // If not using far branches, patch this call directly to dest. set_destination(dest); + } else if (!is_NativeCallTrampolineStub_at(instruction_address() + displacement())) { + // If we want far branches and there isn't a trampoline stub, emit one. + address stub = a.emit_trampoline_stub(instruction_address() - cbuf.insts()->start(), dest); + if (stub == nullptr) { + JVMCI_ERROR("could not emit trampoline stub - code cache is full"); + } + // The relocation created while emitting the stub will ensure this + // call instruction is subsequently patched to call the stub. + } else { + // Not sure how this can be happen but be defensive + JVMCI_ERROR("single-use stub should not exist"); } - - return stub; } +#endif diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp index 75f2797c326..57366ad3c9c 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp @@ -29,6 +29,11 @@ #include "asm/assembler.hpp" #include "runtime/icache.hpp" #include "runtime/os.hpp" +#include "runtime/os.hpp" +#if INCLUDE_JVMCI +#include "jvmci/jvmciExceptions.hpp" +#endif + // We have interfaces for the following instructions: // - NativeInstruction @@ -251,7 +256,9 @@ class NativeCall: public NativeInstruction { void set_destination_mt_safe(address dest, bool assert_lock = true); address get_trampoline(); - address trampoline_jump(CodeBuffer &cbuf, address dest); +#if INCLUDE_JVMCI + void trampoline_jump(CodeBuffer &cbuf, address dest, JVMCI_TRAPS); +#endif }; inline NativeCall* nativeCall_at(address address) { diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index da1584d7969..722dacfb127 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -2359,10 +2359,9 @@ void SharedRuntime::generate_deopt_blob() { Label retaddr; __ set_last_Java_frame(sp, noreg, retaddr, rscratch1); -#ifdef ASSERT0 +#ifdef ASSERT { Label L; - __ ldr(rscratch1, Address(rthread, - JavaThread::last_Java_fp_offset())); + __ ldr(rscratch1, Address(rthread, JavaThread::last_Java_fp_offset())); __ cbz(rscratch1, L); __ stop("SharedRuntime::generate_deopt_blob: last_Java_fp not cleared"); __ bind(L); diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 8c3e165ab8e..a197c781f2d 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -2964,6 +2964,22 @@ class StubGenerator: public StubCodeGenerator { return start; } + // Big-endian 128-bit + 64-bit -> 128-bit addition. + // Inputs: 128-bits. in is preserved. + // The least-significant 64-bit word is in the upper dword of the vector + // inc (the 64-bit increment) is preserved. Its lower dword must be zero + // Output: result + void be_add_128_64(FloatRegister result, FloatRegister in, + FloatRegister inc, FloatRegister tmp) { + assert_different_registers(result, tmp, inc); + + __ addv(result, __ T2D, in, inc); // Add inc to the least-significant dword of input + __ cmhi(tmp, __ T2D, inc, result); // Check for result overflowing + __ ins(tmp, __ D, tmp, 0, 1); // Move LSD of comparison result to MSD + __ ins(tmp, __ D, inc, 1, 0); // Move 0 to LSD of comparison result + __ subv(result, __ T2D, result, tmp); // Subtract -1 from MSD if there was an overflow + } + // CTR AES crypt. // Arguments: // @@ -3073,13 +3089,16 @@ class StubGenerator: public StubCodeGenerator { // Setup the counter __ movi(v4, __ T4S, 0); __ movi(v5, __ T4S, 1); - __ ins(v4, __ S, v5, 3, 3); // v4 contains { 0, 0, 0, 1 } + __ ins(v4, __ S, v5, 2, 2); // v4 contains { 0, 1 } - __ ld1(v0, __ T16B, counter); // Load the counter into v0 - __ rev32(v16, __ T16B, v0); - __ addv(v16, __ T4S, v16, v4); - __ rev32(v16, __ T16B, v16); - __ st1(v16, __ T16B, counter); // Save the incremented counter back + // 128-bit big-endian increment + __ ld1(v0, __ T16B, counter); + __ rev64(v16, __ T16B, v0); + be_add_128_64(v16, v16, v4, /*tmp*/v5); + __ rev64(v16, __ T16B, v16); + __ st1(v16, __ T16B, counter); + // Previous counter value is in v0 + // v4 contains { 0, 1 } { // We have fewer than bulk_width blocks of data left. Encrypt @@ -3111,9 +3130,9 @@ class StubGenerator: public StubCodeGenerator { // Increment the counter, store it back __ orr(v0, __ T16B, v16, v16); - __ rev32(v16, __ T16B, v16); - __ addv(v16, __ T4S, v16, v4); - __ rev32(v16, __ T16B, v16); + __ rev64(v16, __ T16B, v16); + be_add_128_64(v16, v16, v4, /*tmp*/v5); + __ rev64(v16, __ T16B, v16); __ st1(v16, __ T16B, counter); // Save the incremented counter back __ b(inner_loop); @@ -3161,7 +3180,7 @@ class StubGenerator: public StubCodeGenerator { // Keys should already be loaded into the correct registers __ ld1(v0, __ T16B, counter); // v0 contains the first counter - __ rev32(v16, __ T16B, v0); // v16 contains byte-reversed counter + __ rev64(v16, __ T16B, v0); // v16 contains byte-reversed counter // AES/CTR loop { @@ -3171,11 +3190,11 @@ class StubGenerator: public StubCodeGenerator { // Setup the counters __ movi(v8, __ T4S, 0); __ movi(v9, __ T4S, 1); - __ ins(v8, __ S, v9, 3, 3); // v8 contains { 0, 0, 0, 1 } + __ ins(v8, __ S, v9, 2, 2); // v8 contains { 0, 1 } for (FloatRegister f = v0; f < v0 + bulk_width; f++) { - __ rev32(f, __ T16B, v16); - __ addv(v16, __ T4S, v16, v8); + __ rev64(f, __ T16B, v16); + be_add_128_64(v16, v16, v8, /*tmp*/v9); } __ ld1(v8, v9, v10, v11, __ T16B, __ post(in, 4 * 16)); @@ -3203,7 +3222,7 @@ class StubGenerator: public StubCodeGenerator { } // Save the counter back where it goes - __ rev32(v16, __ T16B, v16); + __ rev64(v16, __ T16B, v16); __ st1(v16, __ T16B, counter); __ pop(saved_regs, sp); @@ -6481,10 +6500,16 @@ class StubGenerator: public StubCodeGenerator { __ ret(lr); } - void gen_ldaddal_entry(Assembler::operand_size size) { + void gen_ldadd_entry(Assembler::operand_size size, atomic_memory_order order) { Register prev = r2, addr = c_rarg0, incr = c_rarg1; - __ ldaddal(size, incr, prev, addr); - __ membar(Assembler::StoreStore|Assembler::StoreLoad); + // If not relaxed, then default to conservative. Relaxed is the only + // case we use enough to be worth specializing. + if (order == memory_order_relaxed) { + __ ldadd(size, incr, prev, addr); + } else { + __ ldaddal(size, incr, prev, addr); + __ membar(Assembler::StoreStore|Assembler::StoreLoad); + } if (size == Assembler::xword) { __ mov(r0, prev); } else { @@ -6514,12 +6539,21 @@ class StubGenerator: public StubCodeGenerator { StubCodeMark mark(this, "StubRoutines", "atomic entry points"); address first_entry = __ pc(); - // All memory_order_conservative + // ADD, memory_order_conservative AtomicStubMark mark_fetch_add_4(_masm, &aarch64_atomic_fetch_add_4_impl); - gen_ldaddal_entry(Assembler::word); + gen_ldadd_entry(Assembler::word, memory_order_conservative); AtomicStubMark mark_fetch_add_8(_masm, &aarch64_atomic_fetch_add_8_impl); - gen_ldaddal_entry(Assembler::xword); + gen_ldadd_entry(Assembler::xword, memory_order_conservative); + + // ADD, memory_order_relaxed + AtomicStubMark mark_fetch_add_4_relaxed + (_masm, &aarch64_atomic_fetch_add_4_relaxed_impl); + gen_ldadd_entry(MacroAssembler::word, memory_order_relaxed); + AtomicStubMark mark_fetch_add_8_relaxed + (_masm, &aarch64_atomic_fetch_add_8_relaxed_impl); + gen_ldadd_entry(MacroAssembler::xword, memory_order_relaxed); + // XCHG, memory_order_conservative AtomicStubMark mark_xchg_4(_masm, &aarch64_atomic_xchg_4_impl); gen_swpal_entry(Assembler::word); AtomicStubMark mark_xchg_8_impl(_masm, &aarch64_atomic_xchg_8_impl); @@ -7730,6 +7764,8 @@ void StubGenerator_generate(CodeBuffer* code, bool all) { DEFAULT_ATOMIC_OP(fetch_add, 4, ) DEFAULT_ATOMIC_OP(fetch_add, 8, ) +DEFAULT_ATOMIC_OP(fetch_add, 4, _relaxed) +DEFAULT_ATOMIC_OP(fetch_add, 8, _relaxed) DEFAULT_ATOMIC_OP(xchg, 4, ) DEFAULT_ATOMIC_OP(xchg, 8, ) DEFAULT_ATOMIC_OP(cmpxchg, 1, ) diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 31dfb772759..9f33e7526fe 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -493,36 +493,52 @@ void VM_Version::initialize() { UNSUPPORTED_OPTION(CriticalJNINatives); } -void VM_Version::check_virtualizations() { #if defined(LINUX) - const char* info_file = "/sys/devices/virtual/dmi/id/product_name"; - // check for various strings in the dmi data indicating virtualizations +static bool check_info_file(const char* fpath, + const char* virt1, VirtualizationType vt1, + const char* virt2, VirtualizationType vt2) { char line[500]; - FILE* fp = os::fopen(info_file, "r"); + FILE* fp = os::fopen(fpath, "r"); if (fp == nullptr) { - return; + return false; } while (fgets(line, sizeof(line), fp) != nullptr) { - if (strcasestr(line, "KVM") != 0) { - Abstract_VM_Version::_detected_virtualization = KVM; - break; + if (strcasestr(line, virt1) != 0) { + Abstract_VM_Version::_detected_virtualization = vt1; + fclose(fp); + return true; } - if (strcasestr(line, "VMware") != 0) { - Abstract_VM_Version::_detected_virtualization = VMWare; - break; + if (virt2 != NULL && strcasestr(line, virt2) != 0) { + Abstract_VM_Version::_detected_virtualization = vt2; + fclose(fp); + return true; } } fclose(fp); + return false; +} +#endif + +void VM_Version::check_virtualizations() { +#if defined(LINUX) + const char* pname_file = "/sys/devices/virtual/dmi/id/product_name"; + const char* tname_file = "/sys/hypervisor/type"; + if (check_info_file(pname_file, "KVM", KVM, "VMWare", VMWare)) { + return; + } + check_info_file(tname_file, "Xen", XenPVHVM, NULL, NoDetectedVirtualization); #endif } void VM_Version::print_platform_virtualization_info(outputStream* st) { #if defined(LINUX) - VirtualizationType vrt = VM_Version::get_detected_virtualization(); - if (vrt == KVM) { - st->print_cr("KVM virtualization detected"); - } else if (vrt == VMWare) { - st->print_cr("VMWare virtualization detected"); - } + VirtualizationType vrt = VM_Version::get_detected_virtualization(); + if (vrt == KVM) { + st->print_cr("KVM virtualization detected"); + } else if (vrt == VMWare) { + st->print_cr("VMWare virtualization detected"); + } else if (vrt == XenPVHVM) { + st->print_cr("Xen virtualization detected"); + } #endif } diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 06267046028..d9ade024b91 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -4511,6 +4511,7 @@ instruct storeF( memoryF mem, regF src) %{ // pattern-match out unnecessary membars instruct membar_storestore() %{ match(MemBarStoreStore); + match(StoreStoreFence); ins_cost(4*MEMORY_REF_COST); size(4); diff --git a/src/hotspot/cpu/arm/c2_MacroAssembler_arm.cpp b/src/hotspot/cpu/arm/c2_MacroAssembler_arm.cpp index 2211d5c5fa3..3d205fe7062 100644 --- a/src/hotspot/cpu/arm/c2_MacroAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/c2_MacroAssembler_arm.cpp @@ -160,7 +160,7 @@ void C2_MacroAssembler::fast_unlock(Register Roop, Register Rbox, Register Rscra // Restore the object header bool allow_fallthrough_on_failure = true; bool one_shot = true; - cas_for_lock_release(Rmark, Rbox, Roop, Rscratch, done, allow_fallthrough_on_failure, one_shot); + cas_for_lock_release(Rbox, Rmark, Roop, Rscratch, done, allow_fallthrough_on_failure, one_shot); bind(done); } diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index af9a856ae08..2b7094f71aa 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -828,7 +828,6 @@ int LIR_Assembler::load(Register base, int offset, LIR_Opr to_reg, BasicType typ } else { __ ld(to_reg->as_register(), offset, base); } - __ verify_oop(to_reg->as_register(), FILE_AND_LINE); break; } case T_FLOAT: __ lfs(to_reg->as_float_reg(), offset, base); break; @@ -859,7 +858,6 @@ int LIR_Assembler::load(Register base, Register disp, LIR_Opr to_reg, BasicType } else { __ ldx(to_reg->as_register(), base, disp); } - __ verify_oop(to_reg->as_register(), FILE_AND_LINE); break; } case T_FLOAT: __ lfsx(to_reg->as_float_reg() , base, disp); break; diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 1b6c9c6ba96..06711461666 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -7152,6 +7152,7 @@ instruct membar_release() %{ instruct membar_storestore() %{ match(MemBarStoreStore); + match(StoreStoreFence); ins_cost(4*MEMORY_REF_COST); format %{ "MEMBAR-store-store" %} diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 6610e394e95..0abe0ce4875 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -5048,6 +5048,7 @@ instruct membar_CPUOrder() %{ instruct membar_storestore() %{ match(MemBarStoreStore); + match(StoreStoreFence); ins_cost(0); size(0); format %{ "MEMBAR-storestore (empty)" %} diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp index ba07e84c4c6..6786e1f2635 100644 --- a/src/hotspot/cpu/x86/assembler_x86.cpp +++ b/src/hotspot/cpu/x86/assembler_x86.cpp @@ -2633,6 +2633,16 @@ void Assembler::ktestd(KRegister src1, KRegister src2) { emit_int16((unsigned char)0x99, (0xC0 | encode)); } + +void Assembler::kshiftlbl(KRegister dst, KRegister src, int imm8) { + assert(VM_Version::supports_avx512dq(), ""); + InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false); + int encode = vex_prefix_and_encode(dst->encoding(), 0 , src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int16(0x32, (0xC0 | encode)); + emit_int8(imm8); +} + + void Assembler::movb(Address dst, int imm8) { InstructionMark im(this); prefix(dst); @@ -3948,6 +3958,14 @@ void Assembler::evpcmpuw(KRegister kdst, XMMRegister nds, XMMRegister src, Compa emit_int24(0x3E, (0xC0 | encode), vcc); } +void Assembler::evpcmpuq(KRegister kdst, XMMRegister nds, XMMRegister src, ComparisonPredicate vcc, int vector_len) { + assert(VM_Version::supports_avx512vlbw(), ""); + InstructionAttr attributes(vector_len, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true); + attributes.set_is_evex_instruction(); + int encode = vex_prefix_and_encode(kdst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes); + emit_int24(0x1E, (0xC0 | encode), vcc); +} + void Assembler::evpcmpuw(KRegister kdst, XMMRegister nds, Address src, ComparisonPredicate vcc, int vector_len) { assert(VM_Version::supports_avx512vlbw(), ""); InstructionMark im(this); @@ -6574,6 +6592,19 @@ void Assembler::vpaddq(XMMRegister dst, XMMRegister nds, Address src, int vector emit_operand(dst, src); } +void Assembler::evpaddq(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len) { + assert(VM_Version::supports_evex(), ""); + assert(vector_len == AVX_512bit || VM_Version::supports_avx512vl(), ""); + InstructionAttr attributes(vector_len, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false,/* uses_vl */ true); + attributes.set_is_evex_instruction(); + attributes.set_embedded_opmask_register_specifier(mask); + if (merge) { + attributes.reset_is_clear_context(); + } + int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes); + emit_int16((unsigned char)0xD4, (0xC0 | encode)); +} + void Assembler::psubb(XMMRegister dst, XMMRegister src) { NOT_LP64(assert(VM_Version::supports_sse2(), "")); InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true); @@ -9683,7 +9714,7 @@ void Assembler::prefix(Register dst, Address adr, Prefix p) { if (adr.index_needs_rex()) { assert(false, "prefix(Register dst, Address adr, Prefix p) does not support handling of an X"); } else { - prefix(REX_B); + p = (Prefix)(p | REX_B); } } else { if (adr.index_needs_rex()) { diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp index 3c72c93ad40..6d2a7bde5f8 100644 --- a/src/hotspot/cpu/x86/assembler_x86.hpp +++ b/src/hotspot/cpu/x86/assembler_x86.hpp @@ -1492,6 +1492,8 @@ class Assembler : public AbstractAssembler { void ktestql(KRegister dst, KRegister src); + void kshiftlbl(KRegister dst, KRegister src, int imm8); + void movdl(XMMRegister dst, Register src); void movdl(Register dst, XMMRegister src); void movdl(XMMRegister dst, Address src); @@ -1727,6 +1729,8 @@ class Assembler : public AbstractAssembler { void evpcmpuw(KRegister kdst, XMMRegister nds, XMMRegister src, ComparisonPredicate vcc, int vector_len); void evpcmpuw(KRegister kdst, XMMRegister nds, Address src, ComparisonPredicate vcc, int vector_len); + void evpcmpuq(KRegister kdst, XMMRegister nds, XMMRegister src, ComparisonPredicate vcc, int vector_len); + void pcmpeqw(XMMRegister dst, XMMRegister src); void vpcmpeqw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len); void evpcmpeqw(KRegister kdst, XMMRegister nds, XMMRegister src, int vector_len); @@ -2249,6 +2253,10 @@ class Assembler : public AbstractAssembler { void vpaddd(XMMRegister dst, XMMRegister nds, Address src, int vector_len); void vpaddq(XMMRegister dst, XMMRegister nds, Address src, int vector_len); + // Leaf level assembler routines for masked operations. + void evpaddq(XMMRegister dst, KRegister mask, XMMRegister nds, XMMRegister src, bool merge, int vector_len); +// void evpaddq(XMMRegister dst, KRegister mask, XMMRegister nds, Address src, bool merge, int vector_len); + // Sub packed integers void psubb(XMMRegister dst, XMMRegister src); void psubw(XMMRegister dst, XMMRegister src); diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index 8f16747b6e1..e1c9f022150 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -2094,6 +2094,14 @@ XMMRegister C2_MacroAssembler::get_lane(BasicType typ, XMMRegister dst, XMMRegis } } +void C2_MacroAssembler::movsxl(BasicType typ, Register dst) { + if (typ == T_BYTE) { + movsbl(dst, dst); + } else if (typ == T_SHORT) { + movswl(dst, dst); + } +} + void C2_MacroAssembler::get_elem(BasicType typ, Register dst, XMMRegister src, int elemindex) { int esize = type2aelembytes(typ); int elem_per_lane = 16/esize; @@ -2105,13 +2113,11 @@ void C2_MacroAssembler::get_elem(BasicType typ, Register dst, XMMRegister src, i movq(dst, src); } else { movdl(dst, src); - if (typ == T_BYTE) - movsbl(dst, dst); - else if (typ == T_SHORT) - movswl(dst, dst); + movsxl(typ, dst); } } else { extract(typ, dst, src, eindex); + movsxl(typ, dst); } } diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp index 5527cff637d..01830e5edff 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.hpp @@ -132,6 +132,7 @@ XMMRegister get_lane(BasicType typ, XMMRegister dst, XMMRegister src, int elemindex); void get_elem(BasicType typ, Register dst, XMMRegister src, int elemindex); void get_elem(BasicType typ, XMMRegister dst, XMMRegister src, int elemindex, Register tmp = noreg, XMMRegister vtmp = xnoreg); + void movsxl(BasicType typ, Register dst); // vector test void vectortest(int bt, int vlen, XMMRegister src1, XMMRegister src2, diff --git a/src/hotspot/cpu/x86/crc32c.h b/src/hotspot/cpu/x86/crc32c.h index dfdbd525e77..b2d4a90619d 100644 --- a/src/hotspot/cpu/x86/crc32c.h +++ b/src/hotspot/cpu/x86/crc32c.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,15 +39,15 @@ enum { // based on ubench study using methodology described in // V. Gopal et al. / Fast CRC Computation for iSCSI Polynomial Using CRC32 Instruction April 2011 8 // - // arbitrary value between 27 and 256 - CRC32C_MIDDLE = 8 * 86, + // arbitrary value between 9 and 256 + CRC32C_MIDDLE = 8 * 74, // V. Gopal et al. / Fast CRC Computation for iSCSI Polynomial Using CRC32 Instruction April 2011 9 - // shows that 240 and 1024 are equally good choices as the 216==8*27 + // shows that 240 and 1024 are equally good choices as the 216==8*9*3 // // Selecting the smallest value which resulted in a significant performance improvement over // sequential version - CRC32C_LOW = 8 * 27, + CRC32C_LOW = 8 * 9, CRC32C_NUM_ChunkSizeInBytes = 3, diff --git a/src/hotspot/cpu/x86/frame_x86.cpp b/src/hotspot/cpu/x86/frame_x86.cpp index 3faf6f99db8..aa5628a7f7e 100644 --- a/src/hotspot/cpu/x86/frame_x86.cpp +++ b/src/hotspot/cpu/x86/frame_x86.cpp @@ -64,8 +64,11 @@ bool frame::safe_for_sender(JavaThread *thread) { return false; } - // unextended sp must be within the stack and above or equal sp - if (!thread->is_in_stack_range_incl(unextended_sp, sp)) { + // unextended sp must be within the stack + // Note: sp can be greater than unextended_sp in the case of + // interpreted -> interpreted calls that go through a method handle linker, + // since those pop the last argument (the appendix) from the stack. + if (!thread->is_in_stack_range_incl(unextended_sp, sp - Interpreter::stackElementSize)) { return false; } diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 740189c144f..da4a40ba3f9 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -7937,24 +7937,27 @@ void MacroAssembler::crc32c_ipl_alg2_alt2(Register in_out, Register in1, Registe addl(tmp1, in2); addq(tmp1, in1); - BIND(L_wordByWord); cmpq(in1, tmp1); - jcc(Assembler::greaterEqual, L_byteByByteProlog); - crc32(in_out, Address(in1, 0), 4); - addq(in1, 4); - jmp(L_wordByWord); + jccb(Assembler::greaterEqual, L_byteByByteProlog); + align(16); + BIND(L_wordByWord); + crc32(in_out, Address(in1, 0), 8); + addq(in1, 8); + cmpq(in1, tmp1); + jcc(Assembler::less, L_wordByWord); BIND(L_byteByByteProlog); andl(in2, 0x00000007); movl(tmp2, 1); - BIND(L_byteByByte); cmpl(tmp2, in2); jccb(Assembler::greater, L_exit); + BIND(L_byteByByte); crc32(in_out, Address(in1, 0), 1); incq(in1); incl(tmp2); - jmp(L_byteByByte); + cmpl(tmp2, in2); + jcc(Assembler::lessEqual, L_byteByByte); BIND(L_exit); } diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp index fcde34b2751..dc5c9023c0d 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp @@ -964,6 +964,8 @@ class MacroAssembler: public Assembler { void roundDec(XMMRegister key, int rnum); void lastroundDec(XMMRegister key, int rnum); void ev_load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask); + void ev_add128(XMMRegister xmmdst, XMMRegister xmmsrc1, XMMRegister xmmsrc2, + int vector_len, KRegister ktmp, Register rscratch = noreg); public: void aesecb_encrypt(Register source_addr, Register dest_addr, Register key, Register len); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86_aes.cpp b/src/hotspot/cpu/x86/macroAssembler_x86_aes.cpp index 4f86bc5b695..dcc48a6c1f0 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86_aes.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86_aes.cpp @@ -779,6 +779,19 @@ void MacroAssembler::avx_ghash(Register input_state, Register htbl, vpxor(xmm15, xmm15, xmm15, Assembler::AVX_128bit); } +// Add 128-bit integers in xmmsrc1 to xmmsrc2, then place the result in xmmdst. +// Clobber ktmp and rscratch. +// Used by aesctr_encrypt. +void MacroAssembler::ev_add128(XMMRegister xmmdst, XMMRegister xmmsrc1, XMMRegister xmmsrc2, + int vector_len, KRegister ktmp, Register rscratch) { + vpaddq(xmmdst, xmmsrc1, xmmsrc2, vector_len); + evpcmpuq(ktmp, xmmdst, xmmsrc2, lt, vector_len); // set mask[0/1] bit if addq to dst[0/1] wraps + kshiftlbl(ktmp, ktmp, 1); // mask[1] <- mask[0], mask[0] <- 0, etc + + evpaddq(xmmdst, ktmp, xmmdst, xmm17, /*merge*/true, + vector_len); // dst[1]++ if mask[1] set +} + // AES Counter Mode using VAES instructions void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Register key, Register counter, Register len_reg, Register used, Register used_addr, Register saved_encCounter_start) { @@ -831,19 +844,23 @@ void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Regis //shuffle counter using lbswap_mask vpshufb(xmm8, xmm8, xmm16, Assembler::AVX_512bit); + // Vector value to propagate carries + evmovdquq(xmm17, ExternalAddress(StubRoutines::x86::counter_mask_ones_addr()), Assembler::AVX_512bit, r15); // pre-increment and propagate counter values to zmm9-zmm15 registers. // Linc0 increments the zmm8 by 1 (initial value being 0), Linc4 increments the counters zmm9-zmm15 by 4 // The counter is incremented after each block i.e. 16 bytes is processed; // each zmm register has 4 counter values as its MSB // the counters are incremented in parallel - vpaddd(xmm8, xmm8, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 64), Assembler::AVX_512bit, r15);//linc0 - vpaddd(xmm9, xmm8, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15);//linc4(rip) - vpaddd(xmm10, xmm9, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15);//Linc4(rip) - vpaddd(xmm11, xmm10, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15);//Linc4(rip) - vpaddd(xmm12, xmm11, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15);//Linc4(rip) - vpaddd(xmm13, xmm12, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15);//Linc4(rip) - vpaddd(xmm14, xmm13, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15);//Linc4(rip) - vpaddd(xmm15, xmm14, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15);//Linc4(rip) + evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 64), Assembler::AVX_512bit, r15 /*rscratch*/);//linc0 + ev_add128(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); + evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 128), Assembler::AVX_512bit, r15 /*rscratch*/);//linc4 + ev_add128(xmm9, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); + ev_add128(xmm10, xmm9, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); + ev_add128(xmm11, xmm10, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); + ev_add128(xmm12, xmm11, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); + ev_add128(xmm13, xmm12, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); + ev_add128(xmm14, xmm13, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); + ev_add128(xmm15, xmm14, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15); // load linc32 mask in zmm register.linc32 increments counter by 32 evmovdquq(xmm19, ExternalAddress(StubRoutines::x86::counter_mask_addr() + 256), Assembler::AVX_512bit, r15);//Linc32 @@ -891,21 +908,21 @@ void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Regis // This is followed by incrementing counter values in zmm8-zmm15. // Since we will be processing 32 blocks at a time, the counter is incremented by 32. roundEnc(xmm21, 7); - vpaddq(xmm8, xmm8, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm22, 7); - vpaddq(xmm9, xmm9, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm9, xmm9, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm23, 7); - vpaddq(xmm10, xmm10, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm10, xmm10, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm24, 7); - vpaddq(xmm11, xmm11, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm11, xmm11, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm25, 7); - vpaddq(xmm12, xmm12, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm12, xmm12, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm26, 7); - vpaddq(xmm13, xmm13, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm13, xmm13, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm27, 7); - vpaddq(xmm14, xmm14, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm14, xmm14, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm28, 7); - vpaddq(xmm15, xmm15, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm15, xmm15, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); roundEnc(xmm29, 7); cmpl(rounds, 52); @@ -983,8 +1000,8 @@ void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Regis vpshufb(xmm3, xmm11, xmm16, Assembler::AVX_512bit); evpxorq(xmm3, xmm3, xmm20, Assembler::AVX_512bit); // Increment counter values by 16 - vpaddq(xmm8, xmm8, xmm19, Assembler::AVX_512bit); - vpaddq(xmm9, xmm9, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); + ev_add128/*!!!*/(xmm9, xmm9, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); // AES encode rounds roundEnc(xmm21, 3); roundEnc(xmm22, 3); @@ -1051,7 +1068,7 @@ void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Regis vpshufb(xmm1, xmm9, xmm16, Assembler::AVX_512bit); evpxorq(xmm1, xmm1, xmm20, Assembler::AVX_512bit); // increment counter by 8 - vpaddq(xmm8, xmm8, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); // AES encode roundEnc(xmm21, 1); roundEnc(xmm22, 1); @@ -1109,7 +1126,7 @@ void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Regis vpshufb(xmm0, xmm8, xmm16, Assembler::AVX_512bit); evpxorq(xmm0, xmm0, xmm20, Assembler::AVX_512bit); // Increment counter - vpaddq(xmm8, xmm8, xmm19, Assembler::AVX_512bit); + ev_add128/*!!!*/(xmm8, xmm8, xmm19, Assembler::AVX_512bit, /*ktmp*/k1, r15 /*rscratch*/); vaesenc(xmm0, xmm0, xmm21, Assembler::AVX_512bit); vaesenc(xmm0, xmm0, xmm22, Assembler::AVX_512bit); vaesenc(xmm0, xmm0, xmm23, Assembler::AVX_512bit); @@ -1159,7 +1176,7 @@ void MacroAssembler::aesctr_encrypt(Register src_addr, Register dest_addr, Regis evpxorq(xmm0, xmm0, xmm20, Assembler::AVX_128bit); vaesenc(xmm0, xmm0, xmm21, Assembler::AVX_128bit); // Increment counter by 1 - vpaddq(xmm8, xmm8, xmm19, Assembler::AVX_128bit); + ev_add128/*!!!*/(xmm8, xmm8, xmm19, Assembler::AVX_128bit, /*ktmp*/k1, r15 /*rscratch*/); vaesenc(xmm0, xmm0, xmm22, Assembler::AVX_128bit); vaesenc(xmm0, xmm0, xmm23, Assembler::AVX_128bit); vaesenc(xmm0, xmm0, xmm24, Assembler::AVX_128bit); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index c7634a2e596..d1915b74608 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -4424,7 +4424,19 @@ class StubGenerator: public StubCodeGenerator { return start; } - // Vector AES Counter implementation + // Vector AES Counter implementation + + address counter_mask_ones_addr() { + __ align64(); + StubCodeMark mark(this, "StubRoutines", "counter_mask_addr"); + address start = __ pc(); + for (int i = 0; i < 4; i ++) { + __ emit_data64(0x0000000000000000, relocInfo::none); + __ emit_data64(0x0000000000000001, relocInfo::none); + } + return start; + } + address generate_counterMode_VectorAESCrypt() { __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", "counterMode_AESCrypt"); @@ -7641,6 +7653,7 @@ address generate_avx_ghash_processBlocks() { if (UseAESCTRIntrinsics) { if (VM_Version::supports_avx512_vaes() && VM_Version::supports_avx512bw() && VM_Version::supports_avx512vl()) { StubRoutines::x86::_counter_mask_addr = counter_mask_addr(); + StubRoutines::x86::_counter_mask_ones_addr = counter_mask_ones_addr(); StubRoutines::_counterMode_AESCrypt = generate_counterMode_VectorAESCrypt(); } else { StubRoutines::x86::_counter_shuffle_mask_addr = generate_counter_shuffle_mask(); diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.cpp b/src/hotspot/cpu/x86/stubRoutines_x86.cpp index 4471a5498bb..904a5485ff6 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.cpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.cpp @@ -71,6 +71,7 @@ address StubRoutines::x86::_avx2_shuffle_base64 = NULL; address StubRoutines::x86::_avx2_input_mask_base64 = NULL; address StubRoutines::x86::_avx2_lut_base64 = NULL; address StubRoutines::x86::_counter_mask_addr = NULL; +address StubRoutines::x86::_counter_mask_ones_addr = NULL; address StubRoutines::x86::_lookup_lo_base64 = NULL; address StubRoutines::x86::_lookup_hi_base64 = NULL; address StubRoutines::x86::_lookup_lo_base64url = NULL; diff --git a/src/hotspot/cpu/x86/stubRoutines_x86.hpp b/src/hotspot/cpu/x86/stubRoutines_x86.hpp index 45f4870c372..84eb964d98f 100644 --- a/src/hotspot/cpu/x86/stubRoutines_x86.hpp +++ b/src/hotspot/cpu/x86/stubRoutines_x86.hpp @@ -184,6 +184,7 @@ class x86 { // byte flip mask for sha512 static address _pshuffle_byte_flip_mask_addr_sha512; static address _counter_mask_addr; + static address _counter_mask_ones_addr; // Masks for base64 static address _encoding_table_base64; static address _shuffle_base64; @@ -343,6 +344,7 @@ class x86 { static address base64_avx2_input_mask_addr() { return _avx2_input_mask_base64; } static address base64_avx2_lut_addr() { return _avx2_lut_base64; } static address counter_mask_addr() { return _counter_mask_addr; } + static address counter_mask_ones_addr() { return _counter_mask_ones_addr; } static address base64_vbmi_lookup_lo_addr() { return _lookup_lo_base64; } static address base64_vbmi_lookup_hi_addr() { return _lookup_hi_base64; } static address base64_vbmi_lookup_lo_url_addr() { return _lookup_lo_base64url; } diff --git a/src/hotspot/cpu/x86/x86_32.ad b/src/hotspot/cpu/x86/x86_32.ad index 92cd95e7443..01005c73047 100644 --- a/src/hotspot/cpu/x86/x86_32.ad +++ b/src/hotspot/cpu/x86/x86_32.ad @@ -6646,6 +6646,7 @@ instruct unnecessary_membar_volatile() %{ instruct membar_storestore() %{ match(MemBarStoreStore); + match(StoreStoreFence); ins_cost(0); size(0); diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index 3a95006819a..654b92db5bf 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -6765,6 +6765,7 @@ instruct unnecessary_membar_volatile() instruct membar_storestore() %{ match(MemBarStoreStore); + match(StoreStoreFence); ins_cost(0); size(0); diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index c2d2ecb204b..1539c371c0b 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -904,9 +904,9 @@ bool os::create_attached_thread(JavaThread* thread) { PosixSignals::hotspot_sigmask(thread); log_info(os, thread)("Thread attached (tid: " UINTX_FORMAT ", kernel thread id: " UINTX_FORMAT - ", stack: " PTR_FORMAT " - " PTR_FORMAT " (" SIZE_FORMAT "k) ).", + ", stack: " PTR_FORMAT " - " PTR_FORMAT " (" SIZE_FORMAT "K) ).", os::current_thread_id(), (uintx) kernel_thread_id, - p2i(thread->stack_base()), p2i(thread->stack_end()), thread->stack_size()); + p2i(thread->stack_base()), p2i(thread->stack_end()), thread->stack_size() / K); return true; } diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index d07ebcd7f7b..fe1feae05a8 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -729,9 +729,9 @@ bool os::create_attached_thread(JavaThread* thread) { PosixSignals::hotspot_sigmask(thread); log_info(os, thread)("Thread attached (tid: " UINTX_FORMAT ", pthread id: " UINTX_FORMAT - ", stack: " PTR_FORMAT " - " PTR_FORMAT " (" SIZE_FORMAT "k) ).", + ", stack: " PTR_FORMAT " - " PTR_FORMAT " (" SIZE_FORMAT "K) ).", os::current_thread_id(), (uintx) pthread_self(), - p2i(thread->stack_base()), p2i(thread->stack_end()), thread->stack_size()); + p2i(thread->stack_base()), p2i(thread->stack_end()), thread->stack_size() / K); return true; } @@ -1377,8 +1377,34 @@ void os::print_os_info(outputStream* st) { VM_Version::print_platform_virtualization_info(st); } +#ifdef __APPLE__ +static void print_sysctl_info_string(const char* sysctlkey, outputStream* st, char* buf, size_t size) { + if (sysctlbyname(sysctlkey, buf, &size, NULL, 0) >= 0) { + st->print_cr("%s:%s", sysctlkey, buf); + } +} + +static void print_sysctl_info_uint64(const char* sysctlkey, outputStream* st) { + uint64_t val; + size_t size=sizeof(uint64_t); + if (sysctlbyname(sysctlkey, &val, &size, NULL, 0) >= 0) { + st->print_cr("%s:%llu", sysctlkey, val); + } +} +#endif + void os::pd_print_cpu_info(outputStream* st, char* buf, size_t buflen) { - // Nothing to do for now. +#ifdef __APPLE__ + print_sysctl_info_string("machdep.cpu.brand_string", st, buf, buflen); + print_sysctl_info_uint64("hw.cpufrequency", st); + print_sysctl_info_uint64("hw.cpufrequency_min", st); + print_sysctl_info_uint64("hw.cpufrequency_max", st); + print_sysctl_info_uint64("hw.cachelinesize", st); + print_sysctl_info_uint64("hw.l1icachesize", st); + print_sysctl_info_uint64("hw.l1dcachesize", st); + print_sysctl_info_uint64("hw.l2cachesize", st); + print_sysctl_info_uint64("hw.l3cachesize", st); +#endif } void os::get_summary_cpu_info(char* buf, size_t buflen) { diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 79dd256ac61..e62dcf4f759 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -38,40 +38,26 @@ * on the contents of the mountinfo and cgroup files. */ void CgroupV1Controller::set_subsystem_path(char *cgroup_path) { - char buf[MAXPATHLEN+1]; + stringStream ss; if (_root != NULL && cgroup_path != NULL) { if (strcmp(_root, "/") == 0) { - int buflen; - strncpy(buf, _mount_point, MAXPATHLEN); - buf[MAXPATHLEN-1] = '\0'; + ss.print_raw(_mount_point); if (strcmp(cgroup_path,"/") != 0) { - buflen = strlen(buf); - if ((buflen + strlen(cgroup_path)) > (MAXPATHLEN-1)) { - return; - } - strncat(buf, cgroup_path, MAXPATHLEN-buflen); - buf[MAXPATHLEN-1] = '\0'; + ss.print_raw(cgroup_path); } - _path = os::strdup(buf); + _path = os::strdup(ss.base()); } else { if (strcmp(_root, cgroup_path) == 0) { - strncpy(buf, _mount_point, MAXPATHLEN); - buf[MAXPATHLEN-1] = '\0'; - _path = os::strdup(buf); + ss.print_raw(_mount_point); + _path = os::strdup(ss.base()); } else { char *p = strstr(cgroup_path, _root); if (p != NULL && p == _root) { if (strlen(cgroup_path) > strlen(_root)) { - int buflen; - strncpy(buf, _mount_point, MAXPATHLEN); - buf[MAXPATHLEN-1] = '\0'; - buflen = strlen(buf); - if ((buflen + strlen(cgroup_path) - strlen(_root)) > (MAXPATHLEN-1)) { - return; - } - strncat(buf, cgroup_path + strlen(_root), MAXPATHLEN-buflen); - buf[MAXPATHLEN-1] = '\0'; - _path = os::strdup(buf); + ss.print_raw(_mount_point); + const char* cg_path_sub = cgroup_path + strlen(_root); + ss.print_raw(cg_path_sub); + _path = os::strdup(ss.base()); } } } diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index b1035f89bf0..2456385af7a 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -234,17 +234,12 @@ void CgroupV2Subsystem::print_version_specific_info(outputStream* st) { } char* CgroupV2Controller::construct_path(char* mount_path, char *cgroup_path) { - char buf[MAXPATHLEN+1]; - int buflen; - strncpy(buf, mount_path, MAXPATHLEN); - buf[MAXPATHLEN] = '\0'; - buflen = strlen(buf); - if ((buflen + strlen(cgroup_path)) > MAXPATHLEN) { - return NULL; + stringStream ss; + ss.print_raw(mount_path); + if (strcmp(cgroup_path, "/") != 0) { + ss.print_raw(cgroup_path); } - strncat(buf, cgroup_path, MAXPATHLEN-buflen); - buf[MAXPATHLEN] = '\0'; - return os::strdup(buf); + return os::strdup(ss.base()); } char* CgroupV2Subsystem::pids_max_val() { diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index ba139a3f320..9ba7c41bc31 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -984,7 +984,7 @@ bool os::create_attached_thread(JavaThread* thread) { PosixSignals::hotspot_sigmask(thread); log_info(os, thread)("Thread attached (tid: " UINTX_FORMAT ", pthread id: " UINTX_FORMAT - ", stack: " PTR_FORMAT " - " PTR_FORMAT " (" SIZE_FORMAT "k) ).", + ", stack: " PTR_FORMAT " - " PTR_FORMAT " (" SIZE_FORMAT "K) ).", os::current_thread_id(), (uintx) pthread_self(), p2i(thread->stack_base()), p2i(thread->stack_end()), thread->stack_size()); diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 9b7541792fb..690590b54cf 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -88,6 +88,7 @@ #include #include +#include #include #include #include @@ -625,9 +626,9 @@ bool os::create_attached_thread(JavaThread* thread) { thread->set_osthread(osthread); log_info(os, thread)("Thread attached (tid: " UINTX_FORMAT ", stack: " - PTR_FORMAT " - " PTR_FORMAT " (" SIZE_FORMAT "k) ).", + PTR_FORMAT " - " PTR_FORMAT " (" SIZE_FORMAT "K) ).", os::current_thread_id(), p2i(thread->stack_base()), - p2i(thread->stack_end()), thread->stack_size()); + p2i(thread->stack_end()), thread->stack_size() / K); return true; } @@ -1925,8 +1926,65 @@ void os::win32::print_windows_version(outputStream* st) { st->cr(); } +// Processor Power Information; missing from Windows headers +typedef struct _PROCESSOR_POWER_INFORMATION { + ULONG Number; + ULONG MaxMhz; // max specified clock frequency of the system processor + ULONG CurrentMhz; // max specified processor clock frequency mult. by current processor throttle + ULONG MhzLimit; // max specified processor clock frequency mult. by current processor thermal throttle limit + ULONG MaxIdleState; + ULONG CurrentIdleState; +} PROCESSOR_POWER_INFORMATION; + void os::pd_print_cpu_info(outputStream* st, char* buf, size_t buflen) { - // Nothing to do for now. + int proc_count = os::processor_count(); + // handle potential early cases where processor count is not yet set + if (proc_count < 1) { + SYSTEM_INFO si; + GetSystemInfo(&si); + proc_count = si.dwNumberOfProcessors; + } + + size_t sz_check = sizeof(PROCESSOR_POWER_INFORMATION) * (size_t)proc_count; + NTSTATUS status = ::CallNtPowerInformation(ProcessorInformation, NULL, 0, buf, (ULONG) buflen); + int max_mhz = -1, current_mhz = -1, mhz_limit = -1; + bool same_vals_for_all_cpus = true; + + if (status == ERROR_SUCCESS) { + PROCESSOR_POWER_INFORMATION* pppi = (PROCESSOR_POWER_INFORMATION*) buf; + for (int i = 0; i < proc_count; i++) { + if (i == 0) { + max_mhz = (int) pppi->MaxMhz; + current_mhz = (int) pppi->CurrentMhz; + mhz_limit = (int) pppi->MhzLimit; + } else { + if (max_mhz != (int) pppi->MaxMhz || + current_mhz != (int) pppi->CurrentMhz || + mhz_limit != (int) pppi->MhzLimit) { + same_vals_for_all_cpus = false; + break; + } + } + // avoid iteration in case buf is too small to hold all proc infos + if (sz_check > buflen) break; + pppi++; + } + + if (same_vals_for_all_cpus && max_mhz != -1) { + st->print_cr("Processor Information for all %d processors :", proc_count); + st->print_cr(" Max Mhz: %d, Current Mhz: %d, Mhz Limit: %d", max_mhz, current_mhz, mhz_limit); + return; + } + // differing values, iterate again + pppi = (PROCESSOR_POWER_INFORMATION*) buf; + for (int i = 0; i < proc_count; i++) { + st->print_cr("Processor Information for processor %d", (int) pppi->Number); + st->print_cr(" Max Mhz: %d, Current Mhz: %d, Mhz Limit: %d", + (int) pppi->MaxMhz, (int) pppi->CurrentMhz, (int) pppi->MhzLimit); + if (sz_check > buflen) break; + pppi++; + } + } } void os::get_summary_cpu_info(char* buf, size_t buflen) { diff --git a/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.S b/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.S index 3007587d9c2..9c91942cb33 100644 --- a/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.S +++ b/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.S @@ -47,6 +47,28 @@ aarch64_atomic_fetch_add_4_default_impl: mov w0, w2 ret + .global aarch64_atomic_fetch_add_8_relaxed_default_impl + .align 5 +aarch64_atomic_fetch_add_8_relaxed_default_impl: + prfm pstl1strm, [x0] +0: ldxr x2, [x0] + add x8, x2, x1 + stxr w9, x8, [x0] + cbnz w9, 0b + mov x0, x2 + ret + + .global aarch64_atomic_fetch_add_4_relaxed_default_impl + .align 5 +aarch64_atomic_fetch_add_4_relaxed_default_impl: + prfm pstl1strm, [x0] +0: ldxr w2, [x0] + add w8, w2, w1 + stxr w9, w8, [x0] + cbnz w9, 0b + mov w0, w2 + ret + .globl aarch64_atomic_xchg_4_default_impl .align 5 aarch64_atomic_xchg_4_default_impl: diff --git a/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.hpp b/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.hpp index 316e877ec1f..3208db5b4a6 100644 --- a/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.hpp +++ b/src/hotspot/os_cpu/linux_aarch64/atomic_linux_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2021, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -87,9 +87,14 @@ inline D Atomic::PlatformAdd<4>::fetch_and_add(D volatile* dest, I add_value, atomic_memory_order order) const { STATIC_ASSERT(4 == sizeof(I)); STATIC_ASSERT(4 == sizeof(D)); - D old_value - = atomic_fastcall(aarch64_atomic_fetch_add_4_impl, dest, add_value); - return old_value; + aarch64_atomic_stub_t stub; + switch (order) { + case memory_order_relaxed: + stub = aarch64_atomic_fetch_add_4_relaxed_impl; break; + default: + stub = aarch64_atomic_fetch_add_4_impl; break; + } + return atomic_fastcall(stub, dest, add_value); } template<> @@ -98,9 +103,14 @@ inline D Atomic::PlatformAdd<8>::fetch_and_add(D volatile* dest, I add_value, atomic_memory_order order) const { STATIC_ASSERT(8 == sizeof(I)); STATIC_ASSERT(8 == sizeof(D)); - D old_value - = atomic_fastcall(aarch64_atomic_fetch_add_8_impl, dest, add_value); - return old_value; + aarch64_atomic_stub_t stub; + switch (order) { + case memory_order_relaxed: + stub = aarch64_atomic_fetch_add_8_relaxed_impl; break; + default: + stub = aarch64_atomic_fetch_add_8_impl; break; + } + return atomic_fastcall(stub, dest, add_value); } template<> diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index ca30abf7efa..854b1b310e4 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -4117,6 +4117,7 @@ bool MatchRule::is_ideal_membar() const { !strcmp(_opType,"MemBarReleaseLock") || !strcmp(_opType,"LoadFence" ) || !strcmp(_opType,"StoreFence") || + !strcmp(_opType,"StoreStoreFence") || !strcmp(_opType,"MemBarVolatile") || !strcmp(_opType,"MemBarCPUOrder") || !strcmp(_opType,"MemBarStoreStore") || diff --git a/src/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp index 3795b4e0192..de173c64af1 100644 --- a/src/hotspot/share/c1/c1_Compiler.cpp +++ b/src/hotspot/share/c1/c1_Compiler.cpp @@ -142,6 +142,7 @@ bool Compiler::is_intrinsic_supported(const methodHandle& method) { // since GC can change its value. case vmIntrinsics::_loadFence: case vmIntrinsics::_storeFence: + case vmIntrinsics::_storeStoreFence: case vmIntrinsics::_fullFence: case vmIntrinsics::_floatToRawIntBits: case vmIntrinsics::_intBitsToFloat: diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp index e5e71fab8c1..e9dbf31623c 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -3149,6 +3149,9 @@ void LIRGenerator::do_Intrinsic(Intrinsic* x) { case vmIntrinsics::_storeFence: __ membar_release(); break; + case vmIntrinsics::_storeStoreFence: + __ membar_storestore(); + break; case vmIntrinsics::_fullFence : __ membar(); break; diff --git a/src/hotspot/share/c1/c1_RangeCheckElimination.cpp b/src/hotspot/share/c1/c1_RangeCheckElimination.cpp index f5275eb9d5c..fa1678f71c4 100644 --- a/src/hotspot/share/c1/c1_RangeCheckElimination.cpp +++ b/src/hotspot/share/c1/c1_RangeCheckElimination.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -228,24 +228,23 @@ void RangeCheckEliminator::Visitor::do_ArithmeticOp(ArithmeticOp *ao) { Bound* y_bound = _rce->get_bound(y); if (x_bound->lower() >= 0 && x_bound->lower_instr() == NULL && y->as_ArrayLength() != NULL) { _bound = new Bound(0, NULL, -1, y); - } else if (y->type()->as_IntConstant() && y->type()->as_IntConstant()->value() != 0) { + } else if (x_bound->has_lower() && x_bound->lower() >= 0 && y->type()->as_IntConstant() && + y->type()->as_IntConstant()->value() != 0 && y->type()->as_IntConstant()->value() != min_jint) { // The binary % operator is said to yield the remainder of its operands from an implied division; the // left-hand operand is the dividend and the right-hand operand is the divisor. // - // % operator follows from this rule that the result of the remainder operation can be negative only + // It follows from this rule that the result of the remainder operation can be negative only // if the dividend is negative, and can be positive only if the dividend is positive. Moreover, the - // magnitude of the result is always less than the magnitude of the divisor(See JLS 15.17.3). + // magnitude of the result is always less than the magnitude of the divisor (see JLS 15.17.3). // // So if y is a constant integer and not equal to 0, then we can deduce the bound of remainder operation: // x % -y ==> [0, y - 1] Apply RCE // x % y ==> [0, y - 1] Apply RCE // -x % y ==> [-y + 1, 0] // -x % -y ==> [-y + 1, 0] - if (x_bound->has_lower() && x_bound->lower() >= 0) { - _bound = new Bound(0, NULL, y->type()->as_IntConstant()->value() - 1, NULL); - } else { - _bound = new Bound(); - } + // + // Use the absolute value of y as an upper bound. Skip min_jint because abs(min_jint) is undefined. + _bound = new Bound(0, NULL, abs(y->type()->as_IntConstant()->value()) - 1, NULL); } else { _bound = new Bound(); } @@ -270,17 +269,16 @@ void RangeCheckEliminator::Visitor::do_ArithmeticOp(ArithmeticOp *ao) { Bound * bound = _rce->get_bound(y); if (bound->has_upper() && bound->has_lower()) { - int new_lower = bound->lower() + const_value; - jlong new_lowerl = ((jlong)bound->lower()) + const_value; - int new_upper = bound->upper() + const_value; - jlong new_upperl = ((jlong)bound->upper()) + const_value; - - if (((jlong)new_lower) == new_lowerl && ((jlong)new_upper == new_upperl)) { - Bound *newBound = new Bound(new_lower, bound->lower_instr(), new_upper, bound->upper_instr()); - _bound = newBound; - } else { - // overflow + jint t_lo = bound->lower(); + jint t_hi = bound->upper(); + jint new_lower = java_add(t_lo, const_value); + jint new_upper = java_add(t_hi, const_value); + bool overflow = ((const_value < 0 && (new_lower > t_lo)) || + (const_value > 0 && (new_upper < t_hi))); + if (overflow) { _bound = new Bound(); + } else { + _bound = new Bound(new_lower, bound->lower_instr(), new_upper, bound->upper_instr()); } } else { _bound = new Bound(); @@ -1558,7 +1556,6 @@ void RangeCheckEliminator::Bound::add_assertion(Instruction *instruction, Instru NOT_PRODUCT(ao->set_printable_bci(position->printable_bci())); result = result->insert_after(ao); compare_with = ao; - // TODO: Check that add operation does not overflow! } } assert(compare_with != NULL, "You have to compare with something!"); diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index ca9ef15acb4..c21625b7378 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -2347,28 +2347,28 @@ void FileMapInfo::stop_sharing_and_unmap(const char* msg) { ClassPathEntry** FileMapInfo::_classpath_entries_for_jvmti = NULL; ClassPathEntry* FileMapInfo::get_classpath_entry_for_jvmti(int i, TRAPS) { + if (i == 0) { + // index 0 corresponds to the ClassPathImageEntry which is a globally shared object + // and should never be deleted. + return ClassLoader::get_jrt_entry(); + } ClassPathEntry* ent = _classpath_entries_for_jvmti[i]; if (ent == NULL) { - if (i == 0) { - ent = ClassLoader::get_jrt_entry(); - assert(ent != NULL, "must be"); - } else { - SharedClassPathEntry* scpe = shared_path(i); - assert(scpe->is_jar(), "must be"); // other types of scpe will not produce archived classes + SharedClassPathEntry* scpe = shared_path(i); + assert(scpe->is_jar(), "must be"); // other types of scpe will not produce archived classes - const char* path = scpe->name(); - struct stat st; - if (os::stat(path, &st) != 0) { + const char* path = scpe->name(); + struct stat st; + if (os::stat(path, &st) != 0) { + char *msg = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, strlen(path) + 128); + jio_snprintf(msg, strlen(path) + 127, "error in finding JAR file %s", path); + THROW_MSG_(vmSymbols::java_io_IOException(), msg, NULL); + } else { + ent = ClassLoader::create_class_path_entry(THREAD, path, &st, false, false); + if (ent == NULL) { char *msg = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, strlen(path) + 128); - jio_snprintf(msg, strlen(path) + 127, "error in finding JAR file %s", path); + jio_snprintf(msg, strlen(path) + 127, "error in opening JAR file %s", path); THROW_MSG_(vmSymbols::java_io_IOException(), msg, NULL); - } else { - ent = ClassLoader::create_class_path_entry(THREAD, path, &st, false, false); - if (ent == NULL) { - char *msg = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, strlen(path) + 128); - jio_snprintf(msg, strlen(path) + 127, "error in opening JAR file %s", path); - THROW_MSG_(vmSymbols::java_io_IOException(), msg, NULL); - } } } diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp b/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp index 9e93c4ef926..e5b51ac277c 100644 --- a/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp +++ b/src/hotspot/share/classfile/classLoaderDataGraph.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ #include "classfile/javaClasses.hpp" #include "oops/oop.inline.hpp" #include "runtime/atomic.hpp" +#include "runtime/orderAccess.hpp" inline ClassLoaderData *ClassLoaderDataGraph::find_or_create(Handle loader) { guarantee(loader() != NULL && oopDesc::is_oop(loader()), "Loader must be oop"); @@ -43,29 +44,29 @@ inline ClassLoaderData *ClassLoaderDataGraph::find_or_create(Handle loader) { } size_t ClassLoaderDataGraph::num_instance_classes() { - return _num_instance_classes; + return Atomic::load(&_num_instance_classes); } size_t ClassLoaderDataGraph::num_array_classes() { - return _num_array_classes; + return Atomic::load(&_num_array_classes); } void ClassLoaderDataGraph::inc_instance_classes(size_t count) { - Atomic::add(&_num_instance_classes, count); + Atomic::add(&_num_instance_classes, count, memory_order_relaxed); } void ClassLoaderDataGraph::dec_instance_classes(size_t count) { - assert(count <= _num_instance_classes, "Sanity"); - Atomic::sub(&_num_instance_classes, count); + size_t old_count = Atomic::fetch_and_add(&_num_instance_classes, -count, memory_order_relaxed); + assert(old_count >= count, "Sanity"); } void ClassLoaderDataGraph::inc_array_classes(size_t count) { - Atomic::add(&_num_array_classes, count); + Atomic::add(&_num_array_classes, count, memory_order_relaxed); } void ClassLoaderDataGraph::dec_array_classes(size_t count) { - assert(count <= _num_array_classes, "Sanity"); - Atomic::sub(&_num_array_classes, count); + size_t old_count = Atomic::fetch_and_add(&_num_array_classes, -count, memory_order_relaxed); + assert(old_count >= count, "Sanity"); } bool ClassLoaderDataGraph::should_clean_metaspaces_and_reset() { diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 793beb479ac..9c0309384bb 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -2719,49 +2719,57 @@ void java_lang_Throwable::get_stack_trace_elements(Handle throwable, } } -Handle java_lang_Throwable::get_cause_with_stack_trace(Handle throwable, TRAPS) { - // Call to JVM to fill in the stack trace and clear declaringClassObject to - // not keep classes alive in the stack trace. - // call this: public StackTraceElement[] getStackTrace() +Handle java_lang_Throwable::create_initialization_error(JavaThread* current, Handle throwable) { + // Creates an ExceptionInInitializerError to be recorded as the initialization error when class initialization + // failed due to the passed in 'throwable'. We cannot save 'throwable' directly due to issues with keeping alive + // all objects referenced via its stacktrace. So instead we save a new EIIE instance, with the same message and + // symbolic stacktrace of 'throwable'. assert(throwable.not_null(), "shouldn't be"); - JavaValue result(T_ARRAY); - JavaCalls::call_virtual(&result, throwable, - vmClasses::Throwable_klass(), - vmSymbols::getStackTrace_name(), - vmSymbols::getStackTrace_signature(), - CHECK_NH); - Handle stack_trace(THREAD, result.get_oop()); - assert(stack_trace->is_objArray(), "Should be an array"); - - // Throw ExceptionInInitializerError as the cause with this exception in - // the message and stack trace. - - // Now create the message with the original exception and thread name. + // Now create the message from the original exception and thread name. Symbol* message = java_lang_Throwable::detail_message(throwable()); - ResourceMark rm(THREAD); + ResourceMark rm(current); stringStream st; st.print("Exception %s%s ", throwable()->klass()->name()->as_klass_external_name(), message == nullptr ? "" : ":"); if (message == NULL) { - st.print("[in thread \"%s\"]", THREAD->name()); + st.print("[in thread \"%s\"]", current->name()); } else { - st.print("%s [in thread \"%s\"]", message->as_C_string(), THREAD->name()); + st.print("%s [in thread \"%s\"]", message->as_C_string(), current->name()); } Symbol* exception_name = vmSymbols::java_lang_ExceptionInInitializerError(); - Handle h_cause = Exceptions::new_exception(THREAD, exception_name, st.as_string()); - - // If new_exception returns a different exception while creating the exception, return null. - if (h_cause->klass()->name() != exception_name) { + Handle init_error = Exceptions::new_exception(current, exception_name, st.as_string()); + // If new_exception returns a different exception while creating the exception, + // abandon the attempt to save the initialization error and return null. + if (init_error->klass()->name() != exception_name) { log_info(class, init)("Exception thrown while saving initialization exception %s", - h_cause->klass()->external_name()); + init_error->klass()->external_name()); return Handle(); } - java_lang_Throwable::set_stacktrace(h_cause(), stack_trace()); - // Clear backtrace because the stacktrace should be used instead. - set_backtrace(h_cause(), NULL); - return h_cause; + + // Call to java to fill in the stack trace and clear declaringClassObject to + // not keep classes alive in the stack trace. + // call this: public StackTraceElement[] getStackTrace() + JavaValue result(T_ARRAY); + JavaCalls::call_virtual(&result, throwable, + vmClasses::Throwable_klass(), + vmSymbols::getStackTrace_name(), + vmSymbols::getStackTrace_signature(), + current); + if (!current->has_pending_exception()){ + Handle stack_trace(current, result.get_oop()); + assert(stack_trace->is_objArray(), "Should be an array"); + java_lang_Throwable::set_stacktrace(init_error(), stack_trace()); + // Clear backtrace because the stacktrace should be used instead. + set_backtrace(init_error(), nullptr); + } else { + log_info(class, init)("Exception thrown while getting stack trace for initialization exception %s", + init_error->klass()->external_name()); + current->clear_pending_exception(); + } + + return init_error; } bool java_lang_Throwable::get_top_method_and_bci(oop throwable, Method** method, int* bci) { diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index 583e5473c9a..160e03c3c18 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -569,7 +569,7 @@ class java_lang_Throwable: AllStatic { static void get_stack_trace_elements(Handle throwable, objArrayHandle stack_trace, TRAPS); // For recreating class initialization error exceptions. - static Handle get_cause_with_stack_trace(Handle throwable, TRAPS); + static Handle create_initialization_error(JavaThread* current, Handle throwable); // Printing static void print(oop throwable, outputStream* st); @@ -925,6 +925,8 @@ class java_lang_ref_Reference: AllStatic { static bool is_referent_field(oop obj, ptrdiff_t offset); static inline bool is_final(oop ref); static inline bool is_phantom(oop ref); + static inline bool is_weak(oop ref); + static inline bool is_soft(oop ref); static int referent_offset() { CHECK_INIT(_referent_offset); } static int queue_offset() { CHECK_INIT(_queue_offset); } diff --git a/src/hotspot/share/classfile/javaClasses.inline.hpp b/src/hotspot/share/classfile/javaClasses.inline.hpp index 44c01054566..67c7d121d43 100644 --- a/src/hotspot/share/classfile/javaClasses.inline.hpp +++ b/src/hotspot/share/classfile/javaClasses.inline.hpp @@ -27,6 +27,7 @@ #include "classfile/javaClasses.hpp" +#include "memory/referenceType.hpp" #include "oops/access.inline.hpp" #include "oops/method.hpp" #include "oops/oop.inline.hpp" @@ -130,10 +131,12 @@ bool java_lang_String::is_instance_inlined(oop obj) { // Accessors oop java_lang_ref_Reference::weak_referent_no_keepalive(oop ref) { + assert(java_lang_ref_Reference::is_weak(ref) || java_lang_ref_Reference::is_soft(ref), "must be Weak or Soft Reference"); return ref->obj_field_access(_referent_offset); } oop java_lang_ref_Reference::phantom_referent_no_keepalive(oop ref) { + assert(java_lang_ref_Reference::is_phantom(ref), "must be Phantom Reference"); return ref->obj_field_access(_referent_offset); } @@ -189,6 +192,14 @@ bool java_lang_ref_Reference::is_phantom(oop ref) { return InstanceKlass::cast(ref->klass())->reference_type() == REF_PHANTOM; } +bool java_lang_ref_Reference::is_weak(oop ref) { + return InstanceKlass::cast(ref->klass())->reference_type() == REF_WEAK; +} + +bool java_lang_ref_Reference::is_soft(oop ref) { + return InstanceKlass::cast(ref->klass())->reference_type() == REF_SOFT; +} + inline void java_lang_invoke_CallSite::set_target_volatile(oop site, oop target) { site->obj_field_put_volatile(_target_offset, target); } diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 4c792a86a1c..ced73472040 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -594,7 +594,7 @@ void ErrorContext::stackmap_details(outputStream* ss, const Method* method) cons ClassVerifier::ClassVerifier(JavaThread* current, InstanceKlass* klass) : _thread(current), _previous_symbol(NULL), _symbols(NULL), _exception_type(NULL), - _message(NULL), _method_signatures_table(NULL), _klass(klass) { + _message(NULL), _klass(klass) { _this_type = VerificationType::reference_type(klass->name()); } @@ -625,16 +625,12 @@ void ClassVerifier::verify_class(TRAPS) { // Either verifying both local and remote classes or just remote classes. assert(BytecodeVerificationRemote, "Should not be here"); - // Create hash table containing method signatures. - method_signatures_table_type method_signatures_table; - set_method_signatures_table(&method_signatures_table); - Array* methods = _klass->methods(); int num_methods = methods->length(); for (int index = 0; index < num_methods; index++) { // Check for recursive re-verification before each method. - if (was_recursively_verified()) return; + if (was_recursively_verified()) return; Method* m = methods->at(index); if (m->is_native() || m->is_abstract() || m->is_overpass()) { diff --git a/src/hotspot/share/classfile/verifier.hpp b/src/hotspot/share/classfile/verifier.hpp index a43a14961cf..999391ae696 100644 --- a/src/hotspot/share/classfile/verifier.hpp +++ b/src/hotspot/share/classfile/verifier.hpp @@ -286,7 +286,7 @@ class ClassVerifier : public StackObj { Symbol* _exception_type; char* _message; - method_signatures_table_type* _method_signatures_table; + method_signatures_table_type _method_signatures_table; ErrorContext _error_context; // contains information about an error @@ -438,12 +438,8 @@ class ClassVerifier : public StackObj { Klass* load_class(Symbol* name, TRAPS); - method_signatures_table_type* method_signatures_table() const { - return _method_signatures_table; - } - - void set_method_signatures_table(method_signatures_table_type* method_signatures_table) { - _method_signatures_table = method_signatures_table; + method_signatures_table_type* method_signatures_table() { + return &_method_signatures_table; } int change_sig_to_verificationType( diff --git a/src/hotspot/share/classfile/vmIntrinsics.cpp b/src/hotspot/share/classfile/vmIntrinsics.cpp index 0f9a440b3e6..8ebcf38cb06 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.cpp +++ b/src/hotspot/share/classfile/vmIntrinsics.cpp @@ -397,6 +397,7 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) { case vmIntrinsics::_compareAndExchangeReference: case vmIntrinsics::_compareAndExchangeReferenceAcquire: case vmIntrinsics::_compareAndExchangeReferenceRelease: + case vmIntrinsics::_allocateInstance: if (!InlineUnsafeOps) return true; break; case vmIntrinsics::_getShortUnaligned: @@ -407,7 +408,6 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) { case vmIntrinsics::_putCharUnaligned: case vmIntrinsics::_putIntUnaligned: case vmIntrinsics::_putLongUnaligned: - case vmIntrinsics::_allocateInstance: if (!InlineUnsafeOps || !UseUnalignedAccesses) return true; break; case vmIntrinsics::_hashCode: diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 3422d211284..50c1968ba2e 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -520,6 +520,9 @@ class methodHandle; do_intrinsic(_storeFence, jdk_internal_misc_Unsafe, storeFence_name, storeFence_signature, F_RN) \ do_name( storeFence_name, "storeFence") \ do_alias( storeFence_signature, void_method_signature) \ + do_intrinsic(_storeStoreFence, jdk_internal_misc_Unsafe, storeStoreFence_name, storeStoreFence_signature, F_R) \ + do_name( storeStoreFence_name, "storeStoreFence") \ + do_alias( storeStoreFence_signature, void_method_signature) \ do_intrinsic(_fullFence, jdk_internal_misc_Unsafe, fullFence_name, fullFence_signature, F_RN) \ do_name( fullFence_name, "fullFence") \ do_alias( fullFence_signature, void_method_signature) \ diff --git a/src/hotspot/share/code/codeHeapState.cpp b/src/hotspot/share/code/codeHeapState.cpp index 32bd6ed98b8..3fc1e0ddc10 100644 --- a/src/hotspot/share/code/codeHeapState.cpp +++ b/src/hotspot/share/code/codeHeapState.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "code/codeHeapState.hpp" +#include "code/codeBlob.hpp" #include "compiler/compileBroker.hpp" #include "runtime/safepoint.hpp" #include "runtime/sweeper.hpp" @@ -1152,10 +1153,9 @@ void CodeHeapState::aggregate(outputStream* out, CodeHeap* heap, size_t granular ast->cr(); int reset_val = NMethodSweeper::hotness_counter_reset_val(); - double reverse_free_ratio = (res_size > size) ? (double)res_size/(double)(res_size-size) : (double)res_size; printBox(ast, '-', "Method hotness information at time of this analysis", NULL); ast->print_cr("Highest possible method temperature: %12d", reset_val); - ast->print_cr("Threshold for method to be considered 'cold': %12.3f", -reset_val + reverse_free_ratio * NmethodSweepActivity); + ast->print_cr("Threshold for method to be considered 'cold': %12.3f", -reset_val + (CodeCache::reverse_free_ratio() * NmethodSweepActivity)); if (n_methods > 0) { avgTemp = hotnessAccumulator/n_methods; ast->print_cr("min. hotness = %6d", minTemp); diff --git a/src/hotspot/share/code/compiledIC.hpp b/src/hotspot/share/code/compiledIC.hpp index 38ec5c5aa63..ca4190abc37 100644 --- a/src/hotspot/share/code/compiledIC.hpp +++ b/src/hotspot/share/code/compiledIC.hpp @@ -338,6 +338,8 @@ class StaticCallInfo { class CompiledStaticCall : public ResourceObj { public: // Code + + // Returns NULL if CodeBuffer::expand fails static address emit_to_interp_stub(CodeBuffer &cbuf, address mark = NULL); static int to_interp_stub_size(); static int to_trampoline_stub_size(); diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 7fe1d05ff1c..d371d0af14c 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -1657,6 +1657,8 @@ void nmethod::post_compiled_method_load_event(JvmtiThreadState* state) { if (is_not_entrant() && can_convert_to_zombie()) { return; } + // Ensure the sweeper can't collect this nmethod until it become "active" with JvmtiThreadState::nmethods_do. + mark_as_seen_on_stack(); } // This is a bad time for a safepoint. We don't want @@ -1829,7 +1831,8 @@ bool nmethod::is_unloading() { // oops in the CompiledMethod, by calling oops_do on it. state_unloading_cycle = current_cycle; - if (is_zombie() || method()->can_be_allocated_in_NonNMethod_space()) { + Method* m = method(); + if (is_zombie() || (m != nullptr && m->can_be_allocated_in_NonNMethod_space())) { // When the nmethod is in NonNMethod space, we may reach here without IsUnloadingBehaviour. // However, we only allow this for special methods which never get unloaded. diff --git a/src/hotspot/share/compiler/compilationPolicy.hpp b/src/hotspot/share/compiler/compilationPolicy.hpp index 6ec0d70817c..75571674fe1 100644 --- a/src/hotspot/share/compiler/compilationPolicy.hpp +++ b/src/hotspot/share/compiler/compilationPolicy.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -251,7 +251,6 @@ class CompilationPolicy : AllStatic { static bool can_be_osr_compiled(const methodHandle& m, int comp_level = CompLevel_any); static bool is_compilation_enabled(); - static void do_safepoint_work() { } static CompileTask* select_task_helper(CompileQueue* compile_queue); // Return initial compile level to use with Xcomp (depends on compilation mode). static void reprofile(ScopeDesc* trap_scope, bool is_osr); diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index c8c42ecdff4..4876b0fa334 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -967,6 +967,21 @@ JavaThread* CompileBroker::make_thread(ThreadType type, jobject thread_handle, C return new_thread; } +static bool trace_compiler_threads() { + LogTarget(Debug, jit, thread) lt; + return TraceCompilerThreads || lt.is_enabled(); +} + +static void print_compiler_threads(stringStream& msg) { + if (TraceCompilerThreads) { + tty->print_cr("%7d %s", (int)tty->time_stamp().milliseconds(), msg.as_string()); + } + LogTarget(Debug, jit, thread) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + ls.print_cr("%s", msg.as_string()); + } +} void CompileBroker::init_compiler_sweeper_threads() { NMethodSweeper::set_sweep_threshold_bytes(static_cast(SweeperThreshold * ReservedCodeCacheSize / 100.0)); @@ -1009,11 +1024,13 @@ void CompileBroker::init_compiler_sweeper_threads() { JavaThread *ct = make_thread(compiler_t, thread_handle, _c2_compile_queue, _compilers[1], THREAD); assert(ct != NULL, "should have been handled for initial thread"); _compilers[1]->set_num_compiler_threads(i + 1); - if (TraceCompilerThreads) { + if (trace_compiler_threads()) { ResourceMark rm; ThreadsListHandle tlh; // get_thread_name() depends on the TLH. assert(tlh.includes(ct), "ct=" INTPTR_FORMAT " exited unexpectedly.", p2i(ct)); - tty->print_cr("Added initial compiler thread %s", ct->get_thread_name()); + stringStream msg; + msg.print("Added initial compiler thread %s", ct->get_thread_name()); + print_compiler_threads(msg); } } } @@ -1030,11 +1047,13 @@ void CompileBroker::init_compiler_sweeper_threads() { JavaThread *ct = make_thread(compiler_t, thread_handle, _c1_compile_queue, _compilers[0], THREAD); assert(ct != NULL, "should have been handled for initial thread"); _compilers[0]->set_num_compiler_threads(i + 1); - if (TraceCompilerThreads) { + if (trace_compiler_threads()) { ResourceMark rm; ThreadsListHandle tlh; // get_thread_name() depends on the TLH. assert(tlh.includes(ct), "ct=" INTPTR_FORMAT " exited unexpectedly.", p2i(ct)); - tty->print_cr("Added initial compiler thread %s", ct->get_thread_name()); + stringStream msg; + msg.print("Added initial compiler thread %s", ct->get_thread_name()); + print_compiler_threads(msg); } } } @@ -1099,10 +1118,12 @@ void CompileBroker::possibly_add_compiler_threads(JavaThread* THREAD) { thread_oop = create_thread_oop(name_buffer, THREAD); } if (HAS_PENDING_EXCEPTION) { - if (TraceCompilerThreads) { + if (trace_compiler_threads()) { ResourceMark rm; - tty->print_cr("JVMCI compiler thread creation failed:"); - PENDING_EXCEPTION->print(); + stringStream msg; + msg.print_cr("JVMCI compiler thread creation failed:"); + PENDING_EXCEPTION->print_on(&msg); + print_compiler_threads(msg); } CLEAR_PENDING_EXCEPTION; break; @@ -1117,12 +1138,14 @@ void CompileBroker::possibly_add_compiler_threads(JavaThread* THREAD) { JavaThread *ct = make_thread(compiler_t, compiler2_object(i), _c2_compile_queue, _compilers[1], THREAD); if (ct == NULL) break; _compilers[1]->set_num_compiler_threads(i + 1); - if (TraceCompilerThreads) { + if (trace_compiler_threads()) { ResourceMark rm; ThreadsListHandle tlh; // get_thread_name() depends on the TLH. assert(tlh.includes(ct), "ct=" INTPTR_FORMAT " exited unexpectedly.", p2i(ct)); - tty->print_cr("Added compiler thread %s (available memory: %dMB, available non-profiled code cache: %dMB)", - ct->get_thread_name(), (int)(available_memory/M), (int)(available_cc_np/M)); + stringStream msg; + msg.print("Added compiler thread %s (available memory: %dMB, available non-profiled code cache: %dMB)", + ct->get_thread_name(), (int)(available_memory/M), (int)(available_cc_np/M)); + print_compiler_threads(msg); } } } @@ -1138,12 +1161,14 @@ void CompileBroker::possibly_add_compiler_threads(JavaThread* THREAD) { JavaThread *ct = make_thread(compiler_t, compiler1_object(i), _c1_compile_queue, _compilers[0], THREAD); if (ct == NULL) break; _compilers[0]->set_num_compiler_threads(i + 1); - if (TraceCompilerThreads) { + if (trace_compiler_threads()) { ResourceMark rm; ThreadsListHandle tlh; // get_thread_name() depends on the TLH. assert(tlh.includes(ct), "ct=" INTPTR_FORMAT " exited unexpectedly.", p2i(ct)); - tty->print_cr("Added compiler thread %s (available memory: %dMB, available profiled code cache: %dMB)", - ct->get_thread_name(), (int)(available_memory/M), (int)(available_cc_p/M)); + stringStream msg; + msg.print("Added compiler thread %s (available memory: %dMB, available profiled code cache: %dMB)", + ct->get_thread_name(), (int)(available_memory/M), (int)(available_cc_p/M)); + print_compiler_threads(msg); } } } @@ -1959,9 +1984,12 @@ void CompileBroker::compiler_thread_loop() { // Access compiler_count under lock to enforce consistency. MutexLocker only_one(CompileThread_lock); if (can_remove(thread, true)) { - if (TraceCompilerThreads) { - tty->print_cr("Removing compiler thread %s after " JLONG_FORMAT " ms idle time", - thread->name(), thread->idle_time_millis()); + if (trace_compiler_threads()) { + ResourceMark rm; + stringStream msg; + msg.print("Removing compiler thread %s after " JLONG_FORMAT " ms idle time", + thread->name(), thread->idle_time_millis()); + print_compiler_threads(msg); } // Free buffer blob, if allocated if (thread->get_buffer_blob() != NULL) { @@ -2905,13 +2933,13 @@ void CompileBroker::print_heapinfo(outputStream* out, const char* function, size ts.update(); // record starting point MutexLocker mu11(function_lock_1, Mutex::_safepoint_check_flag); MutexLocker mu22(function_lock_2, Mutex::_no_safepoint_check_flag); - if ((function_lock_1 != NULL) || (function_lock_1 != NULL)) { + if ((function_lock_1 != NULL) || (function_lock_2 != NULL)) { out->print_cr("\n__ Compile & CodeCache (function) lock wait took %10.3f seconds _________\n", ts.seconds()); } ts.update(); // record starting point CodeCache::aggregate(out, granularity); - if ((function_lock_1 != NULL) || (function_lock_1 != NULL)) { + if ((function_lock_1 != NULL) || (function_lock_2 != NULL)) { out->print_cr("\n__ Compile & CodeCache (function) lock hold took %10.3f seconds _________\n", ts.seconds()); } } diff --git a/src/hotspot/share/compiler/compilerOracle.cpp b/src/hotspot/share/compiler/compilerOracle.cpp index 69a3278736c..5149121c5bc 100644 --- a/src/hotspot/share/compiler/compilerOracle.cpp +++ b/src/hotspot/share/compiler/compilerOracle.cpp @@ -308,6 +308,8 @@ static void register_command(TypedMethodOptionMatcher* matcher, if (option == CompileCommand::Blackhole && !UnlockExperimentalVMOptions) { warning("Blackhole compile option is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions"); + // Delete matcher as we don't keep it + delete matcher; return; } diff --git a/src/hotspot/share/gc/epsilon/epsilonHeap.hpp b/src/hotspot/share/gc/epsilon/epsilonHeap.hpp index 2d90439a1ee..66a668a5317 100644 --- a/src/hotspot/share/gc/epsilon/epsilonHeap.hpp +++ b/src/hotspot/share/gc/epsilon/epsilonHeap.hpp @@ -52,7 +52,7 @@ class EpsilonHeap : public CollectedHeap { static EpsilonHeap* heap(); EpsilonHeap() : - _memory_manager("Epsilon Heap", ""), + _memory_manager("Epsilon Heap"), _space(NULL) {}; virtual Name kind() const { diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.cpp b/src/hotspot/share/gc/g1/g1BarrierSet.cpp index d6e103d478e..d22dc6fb601 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.cpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.cpp @@ -62,21 +62,18 @@ G1BarrierSet::G1BarrierSet(G1CardTable* card_table) : _shared_dirty_card_queue(&_dirty_card_queue_set) {} -void G1BarrierSet::enqueue(oop pre_val) { - // Nulls should have been already filtered. - assert(oopDesc::is_oop(pre_val, true), "Error"); - SATBMarkQueue& queue = G1ThreadLocalData::satb_mark_queue(Thread::current()); - G1BarrierSet::satb_mark_queue_set().enqueue(queue, pre_val); -} - template void G1BarrierSet::write_ref_array_pre_work(T* dst, size_t count) { - if (!_satb_mark_queue_set.is_active()) return; + G1SATBMarkQueueSet& queue_set = G1BarrierSet::satb_mark_queue_set(); + if (!queue_set.is_active()) return; + + SATBMarkQueue& queue = G1ThreadLocalData::satb_mark_queue(Thread::current()); + T* elem_ptr = dst; for (size_t i = 0; i < count; i++, elem_ptr++) { T heap_oop = RawAccess<>::oop_load(elem_ptr); if (!CompressedOops::is_null(heap_oop)) { - enqueue(CompressedOops::decode_not_null(heap_oop)); + queue_set.enqueue_known_active(queue, CompressedOops::decode_not_null(heap_oop)); } } } diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.hpp index 8d009a9e19f..c60628fe7ff 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.hpp @@ -58,10 +58,12 @@ class G1BarrierSet: public CardTableBarrierSet { } // Add "pre_val" to a set of objects that may have been disconnected from the - // pre-marking object graph. - static void enqueue(oop pre_val); + // pre-marking object graph. Prefer the version that takes location, as it + // can avoid touching the heap unnecessarily. + template static void enqueue(T* dst); + static void enqueue_preloaded(oop pre_val); - static void enqueue_if_weak(DecoratorSet decorators, oop value); + static void enqueue_preloaded_if_weak(DecoratorSet decorators, oop value); template void write_ref_array_pre_work(T* dst, size_t count); virtual void write_ref_array_pre(oop* dst, size_t count, bool dest_uninitialized); diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp index 8fa29544ca4..f7a6fd3ee5a 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp @@ -28,11 +28,35 @@ #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1CardTable.hpp" +#include "gc/g1/g1ThreadLocalData.hpp" #include "gc/shared/accessBarrierSupport.inline.hpp" #include "oops/access.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oop.hpp" +inline void G1BarrierSet::enqueue_preloaded(oop pre_val) { + // Nulls should have been already filtered. + assert(oopDesc::is_oop(pre_val, true), "Error"); + + G1SATBMarkQueueSet& queue_set = G1BarrierSet::satb_mark_queue_set(); + if (!queue_set.is_active()) return; + + SATBMarkQueue& queue = G1ThreadLocalData::satb_mark_queue(Thread::current()); + queue_set.enqueue_known_active(queue, pre_val); +} + +template +inline void G1BarrierSet::enqueue(T* dst) { + G1SATBMarkQueueSet& queue_set = G1BarrierSet::satb_mark_queue_set(); + if (!queue_set.is_active()) return; + + T heap_oop = RawAccess::oop_load(dst); + if (!CompressedOops::is_null(heap_oop)) { + SATBMarkQueue& queue = G1ThreadLocalData::satb_mark_queue(Thread::current()); + queue_set.enqueue_known_active(queue, CompressedOops::decode_not_null(heap_oop)); + } +} + template inline void G1BarrierSet::write_ref_field_pre(T* field) { if (HasDecorator::value || @@ -40,10 +64,7 @@ inline void G1BarrierSet::write_ref_field_pre(T* field) { return; } - T heap_oop = RawAccess::oop_load(field); - if (!CompressedOops::is_null(heap_oop)) { - enqueue(CompressedOops::decode_not_null(heap_oop)); - } + enqueue(field); } template @@ -55,7 +76,7 @@ inline void G1BarrierSet::write_ref_field_post(T* field, oop new_val) { } } -inline void G1BarrierSet::enqueue_if_weak(DecoratorSet decorators, oop value) { +inline void G1BarrierSet::enqueue_preloaded_if_weak(DecoratorSet decorators, oop value) { assert((decorators & ON_UNKNOWN_OOP_REF) == 0, "Reference strength must be known"); // Loading from a weak or phantom reference needs enqueueing, as // the object may not have been reachable (part of the snapshot) @@ -65,7 +86,7 @@ inline void G1BarrierSet::enqueue_if_weak(DecoratorSet decorators, oop value) { const bool needs_enqueue = (!peek && !on_strong_oop_ref); if (needs_enqueue && value != NULL) { - enqueue(value); + enqueue_preloaded(value); } } @@ -74,7 +95,7 @@ template inline oop G1BarrierSet::AccessBarrier:: oop_load_not_in_heap(T* addr) { oop value = ModRef::oop_load_not_in_heap(addr); - enqueue_if_weak(decorators, value); + enqueue_preloaded_if_weak(decorators, value); return value; } @@ -83,7 +104,7 @@ template inline oop G1BarrierSet::AccessBarrier:: oop_load_in_heap(T* addr) { oop value = ModRef::oop_load_in_heap(addr); - enqueue_if_weak(decorators, value); + enqueue_preloaded_if_weak(decorators, value); return value; } @@ -91,7 +112,7 @@ template inline oop G1BarrierSet::AccessBarrier:: oop_load_in_heap_at(oop base, ptrdiff_t offset) { oop value = ModRef::oop_load_in_heap_at(base, offset); - enqueue_if_weak(AccessBarrierSupport::resolve_possibly_unknown_oop_ref_strength(base, offset), value); + enqueue_preloaded_if_weak(AccessBarrierSupport::resolve_possibly_unknown_oop_ref_strength(base, offset), value); return value; } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index cc2e679925a..3d4a5d4da97 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -2292,7 +2292,7 @@ void G1CollectedHeap::object_iterate_parallel(ObjectClosure* cl, uint worker_id, } void G1CollectedHeap::keep_alive(oop obj) { - G1BarrierSet::enqueue(obj); + G1BarrierSet::enqueue_preloaded(obj); } void G1CollectedHeap::heap_region_iterate(HeapRegionClosure* cl) const { diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp index f03bbb42a7b..948e564e50e 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp @@ -56,26 +56,6 @@ G1ConcurrentMarkThread::G1ConcurrentMarkThread(G1ConcurrentMark* cm) : create_and_start(); } -class CMRemark : public VoidClosure { - G1ConcurrentMark* _cm; -public: - CMRemark(G1ConcurrentMark* cm) : _cm(cm) {} - - void do_void(){ - _cm->remark(); - } -}; - -class CMCleanup : public VoidClosure { - G1ConcurrentMark* _cm; -public: - CMCleanup(G1ConcurrentMark* cm) : _cm(cm) {} - - void do_void(){ - _cm->cleanup(); - } -}; - double G1ConcurrentMarkThread::mmu_delay_end(G1Policy* policy, bool remark) { // There are 3 reasons to use SuspendibleThreadSetJoiner. // 1. To avoid concurrency problem. @@ -239,8 +219,7 @@ bool G1ConcurrentMarkThread::subphase_delay_to_keep_mmu_before_remark() { bool G1ConcurrentMarkThread::subphase_remark() { ConcurrentGCBreakpoints::at("BEFORE MARKING COMPLETED"); - CMRemark cl(_cm); - VM_G1Concurrent op(&cl, "Pause Remark"); + VM_G1PauseRemark op; VMThread::execute(&op); return _cm->has_aborted(); } @@ -257,8 +236,7 @@ bool G1ConcurrentMarkThread::phase_delay_to_keep_mmu_before_cleanup() { } bool G1ConcurrentMarkThread::phase_cleanup() { - CMCleanup cl(_cm); - VM_G1Concurrent op(&cl, "Pause Cleanup"); + VM_G1PauseCleanup op; VMThread::execute(&op); return _cm->has_aborted(); } diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index 0522528ebef..78bc3e84bac 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -304,7 +304,10 @@ void G1FullCollector::phase1_mark_live_objects() { _heap->complete_cleaning(&_is_alive, purged_class); } - scope()->tracer()->report_object_count_after_gc(&_is_alive); + { + GCTraceTime(Debug, gc, phases) debug("Report Object Count", scope()->timer()); + scope()->tracer()->report_object_count_after_gc(&_is_alive); + } } void G1FullCollector::phase2_prepare_compaction() { diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp index fd21cc1f47b..7e9005068b9 100644 --- a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp +++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp @@ -86,8 +86,8 @@ class G1OldGenerationCounters : public G1GenerationCounters { G1MonitoringSupport::G1MonitoringSupport(G1CollectedHeap* g1h) : _g1h(g1h), - _incremental_memory_manager("G1 Young Generation", "end of minor GC"), - _full_gc_memory_manager("G1 Old Generation", "end of major GC"), + _incremental_memory_manager("G1 Young Generation"), + _full_gc_memory_manager("G1 Old Generation"), _eden_space_pool(NULL), _survivor_space_pool(NULL), _old_gen_pool(NULL), @@ -345,5 +345,5 @@ MemoryUsage G1MonitoringSupport::old_gen_memory_usage(size_t initial_size, size_ G1MonitoringScope::G1MonitoringScope(G1MonitoringSupport* g1mm, bool full_gc, bool all_memory_pools_affected) : _tcs(full_gc ? g1mm->_full_collection_counters : g1mm->_incremental_collection_counters), _tms(full_gc ? &g1mm->_full_gc_memory_manager : &g1mm->_incremental_memory_manager, - G1CollectedHeap::heap()->gc_cause(), all_memory_pools_affected) { + G1CollectedHeap::heap()->gc_cause(), full_gc ? "end of major GC" : "end of minor GC", all_memory_pools_affected) { } diff --git a/src/hotspot/share/gc/g1/g1ServiceThread.cpp b/src/hotspot/share/gc/g1/g1ServiceThread.cpp index 7b39b7d2808..cb6b897fcb0 100644 --- a/src/hotspot/share/gc/g1/g1ServiceThread.cpp +++ b/src/hotspot/share/gc/g1/g1ServiceThread.cpp @@ -112,12 +112,10 @@ void G1ServiceThread::notify() { } void G1ServiceThread::sleep_before_next_cycle() { + MonitorLocker ml(&_monitor, Mutex::_no_safepoint_check_flag); if (should_terminate()) { return; - } - - MonitorLocker ml(&_monitor, Mutex::_no_safepoint_check_flag); - if (_task_queue.is_empty()) { + } else if (_task_queue.is_empty()) { // Sleep until new task is registered if no tasks available. log_trace(gc, task)("G1 Service Thread (wait for new tasks)"); ml.wait(0); diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp index a3663301a3f..59004af9a90 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.cpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp @@ -159,7 +159,7 @@ void VM_G1CollectForAllocation::doit() { } } -void VM_G1Concurrent::doit() { +void VM_G1PauseConcurrent::doit() { GCIdMark gc_id_mark(_gc_id); GCTraceCPUTime tcpu; G1CollectedHeap* g1h = G1CollectedHeap::heap(); @@ -173,17 +173,28 @@ void VM_G1Concurrent::doit() { TraceCollectorStats tcs(g1h->g1mm()->conc_collection_counters()); SvcGCMarker sgcm(SvcGCMarker::CONCURRENT); IsGCActiveMark x; - _cl->do_void(); + + work(); } -bool VM_G1Concurrent::doit_prologue() { +bool VM_G1PauseConcurrent::doit_prologue() { Heap_lock->lock(); return true; } -void VM_G1Concurrent::doit_epilogue() { +void VM_G1PauseConcurrent::doit_epilogue() { if (Universe::has_reference_pending_list()) { Heap_lock->notify_all(); } Heap_lock->unlock(); } + +void VM_G1PauseRemark::work() { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + g1h->concurrent_mark()->remark(); +} + +void VM_G1PauseCleanup::work() { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + g1h->concurrent_mark()->cleanup(); +} diff --git a/src/hotspot/share/gc/g1/g1VMOperations.hpp b/src/hotspot/share/gc/g1/g1VMOperations.hpp index 317ab76a5a9..b5bd8eb9166 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.hpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.hpp @@ -84,18 +84,33 @@ class VM_G1CollectForAllocation : public VM_CollectForAllocation { }; // Concurrent G1 stop-the-world operations such as remark and cleanup. -class VM_G1Concurrent : public VM_Operation { - VoidClosure* _cl; - const char* _message; +class VM_G1PauseConcurrent : public VM_Operation { uint _gc_id; + const char* _message; + +protected: + VM_G1PauseConcurrent(const char* message) : + _gc_id(GCId::current()), _message(message) { } + virtual void work() = 0; public: - VM_G1Concurrent(VoidClosure* cl, const char* message) : - _cl(cl), _message(message), _gc_id(GCId::current()) { } - virtual VMOp_Type type() const { return VMOp_G1Concurrent; } - virtual void doit(); - virtual bool doit_prologue(); - virtual void doit_epilogue(); + bool doit_prologue() override; + void doit_epilogue() override; + void doit() override; +}; + +class VM_G1PauseRemark : public VM_G1PauseConcurrent { +public: + VM_G1PauseRemark() : VM_G1PauseConcurrent("Pause Remark") { } + VMOp_Type type() const override { return VMOp_G1PauseRemark; } + void work() override; +}; + +class VM_G1PauseCleanup : public VM_G1PauseConcurrent { +public: + VM_G1PauseCleanup() : VM_G1PauseConcurrent("Pause Cleanup") { } + VMOp_Type type() const override { return VMOp_G1PauseCleanup; } + void work() override; }; #endif // SHARE_GC_G1_G1VMOPERATIONS_HPP diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index 272c1e28c3a..16c7d91c41a 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -150,8 +150,8 @@ void ParallelScavengeHeap::initialize_serviceability() { "PS Old Gen", true /* support_usage_threshold */); - _young_manager = new GCMemoryManager("PS Scavenge", "end of minor GC"); - _old_manager = new GCMemoryManager("PS MarkSweep", "end of major GC"); + _young_manager = new GCMemoryManager("PS Scavenge"); + _old_manager = new GCMemoryManager("PS MarkSweep"); _old_manager->add_pool(_eden_pool); _old_manager->add_pool(_survivor_pool); diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index 33554561f89..8cf13bd1d75 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -1773,7 +1773,7 @@ bool PSParallelCompact::invoke_no_policy(bool maximum_heap_compaction) { heap->pre_full_gc_dump(&_gc_timer); TraceCollectorStats tcs(counters()); - TraceMemoryManagerStats tms(heap->old_gc_manager(), gc_cause); + TraceMemoryManagerStats tms(heap->old_gc_manager(), gc_cause, "end of major GC"); if (log_is_enabled(Debug, gc, heap, exit)) { accumulated_time()->start(); @@ -2137,7 +2137,10 @@ void PSParallelCompact::marking_phase(ParCompactionManager* cm, JVMCI_ONLY(JVMCI::do_unloading(purged_class)); } - _gc_tracer.report_object_count_after_gc(is_alive_closure()); + { + GCTraceTime(Debug, gc, phases) tm("Report Object Count", &_gc_timer); + _gc_tracer.report_object_count_after_gc(is_alive_closure()); + } } #ifdef ASSERT diff --git a/src/hotspot/share/gc/parallel/psScavenge.cpp b/src/hotspot/share/gc/parallel/psScavenge.cpp index f324e4a9b73..20d5caaa84d 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.cpp +++ b/src/hotspot/share/gc/parallel/psScavenge.cpp @@ -420,7 +420,7 @@ bool PSScavenge::invoke_no_policy() { GCTraceCPUTime tcpu; GCTraceTime(Info, gc) tm("Pause Young", NULL, gc_cause, true); TraceCollectorStats tcs(counters()); - TraceMemoryManagerStats tms(heap->young_gc_manager(), gc_cause); + TraceMemoryManagerStats tms(heap->young_gc_manager(), gc_cause, "end of minor GC"); if (log_is_enabled(Debug, gc, heap, exit)) { accumulated_time()->start(); diff --git a/src/hotspot/share/gc/serial/genMarkSweep.cpp b/src/hotspot/share/gc/serial/genMarkSweep.cpp index 38ebfef2803..5a8d2845bef 100644 --- a/src/hotspot/share/gc/serial/genMarkSweep.cpp +++ b/src/hotspot/share/gc/serial/genMarkSweep.cpp @@ -230,7 +230,10 @@ void GenMarkSweep::mark_sweep_phase1(bool clear_all_softrefs) { JVMCI_ONLY(JVMCI::do_unloading(purged_class)); } - gc_tracer()->report_object_count_after_gc(&is_alive); + { + GCTraceTime(Debug, gc, phases) tm_m("Report Object Count", gc_timer()); + gc_tracer()->report_object_count_after_gc(&is_alive); + } } diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index d9a8812d580..0ea8e85e5ad 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -42,8 +42,8 @@ SerialHeap::SerialHeap() : _eden_pool(NULL), _survivor_pool(NULL), _old_pool(NULL) { - _young_manager = new GCMemoryManager("Copy", "end of minor GC"); - _old_manager = new GCMemoryManager("MarkSweepCompact", "end of major GC"); + _young_manager = new GCMemoryManager("Copy"); + _old_manager = new GCMemoryManager("MarkSweepCompact"); } void SerialHeap::initialize_serviceability() { diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp index b64a4bf9b98..d5d4e57596a 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp @@ -290,7 +290,7 @@ class BarrierSetC2: public CHeapObj { virtual void verify_gc_barriers(Compile* compile, CompilePhase phase) const {} #endif - virtual bool final_graph_reshaping(Compile* compile, Node* n, uint opcode) const { return false; } + virtual bool final_graph_reshaping(Compile* compile, Node* n, uint opcode, Unique_Node_List& dead_nodes) const { return false; } virtual bool escape_add_to_con_graph(ConnectionGraph* conn_graph, PhaseGVN* gvn, Unique_Node_List* delayed_worklist, Node* n, uint opcode) const { return false; } virtual bool escape_add_final_edges(ConnectionGraph* conn_graph, PhaseGVN* gvn, Node* n, uint opcode) const { return false; } diff --git a/src/hotspot/share/gc/shared/genCollectedHeap.cpp b/src/hotspot/share/gc/shared/genCollectedHeap.cpp index 528aacf3b5b..411c1dc06ba 100644 --- a/src/hotspot/share/gc/shared/genCollectedHeap.cpp +++ b/src/hotspot/share/gc/shared/genCollectedHeap.cpp @@ -444,7 +444,7 @@ void GenCollectedHeap::collect_generation(Generation* gen, bool full, size_t siz FormatBuffer<> title("Collect gen: %s", gen->short_name()); GCTraceTime(Trace, gc, phases) t1(title); TraceCollectorStats tcs(gen->counters()); - TraceMemoryManagerStats tmms(gen->gc_manager(), gc_cause()); + TraceMemoryManagerStats tmms(gen->gc_manager(), gc_cause(), heap()->is_young_gen(gen) ? "end of minor GC" : "end of major GC"); gen->stat_record()->invocations++; gen->stat_record()->accumulated_time.start(); diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index b18e4c310d7..5af0b91ec19 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -1124,7 +1124,7 @@ bool ShenandoahBarrierSetC2::has_only_shenandoah_wb_pre_uses(Node* n) { return n->outcnt() > 0; } -bool ShenandoahBarrierSetC2::final_graph_reshaping(Compile* compile, Node* n, uint opcode) const { +bool ShenandoahBarrierSetC2::final_graph_reshaping(Compile* compile, Node* n, uint opcode, Unique_Node_List& dead_nodes) const { switch (opcode) { case Op_CallLeaf: case Op_CallLeafNoFP: { diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp index 656b701f278..add752a413a 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp @@ -139,7 +139,7 @@ class ShenandoahBarrierSetC2 : public BarrierSetC2 { #endif virtual Node* ideal_node(PhaseGVN* phase, Node* n, bool can_reshape) const; - virtual bool final_graph_reshaping(Compile* compile, Node* n, uint opcode) const; + virtual bool final_graph_reshaping(Compile* compile, Node* n, uint opcode, Unique_Node_List& dead_nodes) const; virtual bool escape_add_to_con_graph(ConnectionGraph* conn_graph, PhaseGVN* gvn, Unique_Node_List* delayed_worklist, Node* n, uint opcode) const; virtual bool escape_add_final_edges(ConnectionGraph* conn_graph, PhaseGVN* gvn, Node* n, uint opcode) const; diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.cpp index 0c428246169..cc9d8c26d44 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.cpp @@ -40,7 +40,6 @@ void ShenandoahIUMode::initialize_flags() const { FLAG_SET_DEFAULT(ClassUnloadingWithConcurrentMark, false); if (ClassUnloading) { - FLAG_SET_DEFAULT(ShenandoahSuspendibleWorkers, true); FLAG_SET_DEFAULT(VerifyBeforeExit, false); } diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp index 06ed25e59c6..b1770fa0b71 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp @@ -35,7 +35,6 @@ void ShenandoahSATBMode::initialize_flags() const { if (ClassUnloading) { - FLAG_SET_DEFAULT(ShenandoahSuspendibleWorkers, true); FLAG_SET_DEFAULT(VerifyBeforeExit, false); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index 3cb596a887d..860e014f029 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -70,7 +70,7 @@ inline oop ShenandoahBarrierSet::load_reference_barrier_mutator(oop obj, T* load if (load_addr != NULL && fwd != obj) { // Since we are here and we know the load address, update the reference. - ShenandoahHeap::cas_oop(fwd, load_addr, obj); + ShenandoahHeap::atomic_update_oop(fwd, load_addr, obj); } return fwd; @@ -125,7 +125,7 @@ inline oop ShenandoahBarrierSet::load_reference_barrier(DecoratorSet decorators, oop fwd = load_reference_barrier(obj); if (ShenandoahSelfFixing && load_addr != NULL && fwd != obj) { // Since we are here and we know the load address, update the reference. - ShenandoahHeap::cas_oop(fwd, load_addr, obj); + ShenandoahHeap::atomic_update_oop(fwd, load_addr, obj); } return fwd; @@ -353,7 +353,7 @@ void ShenandoahBarrierSet::arraycopy_work(T* src, size_t count) { fwd = _heap->evacuate_object(obj, thread); } assert(obj != fwd || _heap->cancelled_gc(), "must be forwarded"); - oop witness = ShenandoahHeap::cas_oop(fwd, elem_ptr, o); + ShenandoahHeap::atomic_update_oop(fwd, elem_ptr, o); obj = fwd; } if (ENQUEUE && !ctx->is_marked_strong(obj)) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetClone.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetClone.inline.hpp index 69785a61060..71cafcd49dc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetClone.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetClone.inline.hpp @@ -54,7 +54,7 @@ class ShenandoahUpdateRefsForOopClosure: public BasicOopIterateClosure { fwd = _heap->evacuate_object(obj, _thread); } assert(obj != fwd || _heap->cancelled_gc(), "must be forwarded"); - ShenandoahHeap::cas_oop(fwd, p, o); + ShenandoahHeap::atomic_update_oop(fwd, p, o); obj = fwd; } if (ENQUEUE) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp index de59519d605..d03c029a0da 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp @@ -161,7 +161,7 @@ void ShenandoahEvacuateUpdateRootsClosure::do_oop_work(T* p, Thread* t) { if (resolved == obj) { resolved = _heap->evacuate_object(obj, t); } - _heap->cas_oop(resolved, p, o); + ShenandoahHeap::atomic_update_oop(resolved, p, o); } } } @@ -207,7 +207,7 @@ void ShenandoahCleanUpdateWeakOopsClosure::do_oo _keep_alive->do_oop(p); } else { if (CONCURRENT) { - Atomic::cmpxchg(p, obj, oop()); + ShenandoahHeap::atomic_clear_oop(p, obj); } else { RawAccess::oop_store(p, oop()); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 4dae246302a..5b609b87add 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -698,13 +698,13 @@ void ShenandoahEvacUpdateCleanupOopStorageRootsClosure::do_oop(oop* p) { if (!CompressedOops::is_null(obj)) { if (!_mark_context->is_marked(obj)) { shenandoah_assert_correct(p, obj); - Atomic::cmpxchg(p, obj, oop(NULL)); + ShenandoahHeap::atomic_clear_oop(p, obj); } else if (_evac_in_progress && _heap->in_collection_set(obj)) { oop resolved = ShenandoahBarrierSet::resolve_forwarded_not_null(obj); if (resolved == obj) { resolved = _heap->evacuate_object(obj, _thread); } - Atomic::cmpxchg(p, obj, resolved); + ShenandoahHeap::atomic_update_oop(resolved, p, obj); assert(_heap->cancelled_gc() || _mark_context->is_marked(resolved) && !_heap->in_collection_set(resolved), "Sanity"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index 81371c398d7..dce2fe0f1e0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -53,7 +53,7 @@ ShenandoahControlThread::ShenandoahControlThread() : _requested_gc_cause(GCCause::_no_cause_specified), _degen_point(ShenandoahGC::_degenerated_outside_cycle), _allocs_seen(0) { - + set_name("Shenandoah Control Thread"); reset_gc_id(); create_and_start(); _periodic_task.enroll(); @@ -623,16 +623,6 @@ size_t ShenandoahControlThread::get_gc_id() { return Atomic::load(&_gc_id); } -void ShenandoahControlThread::print() const { - print_on(tty); -} - -void ShenandoahControlThread::print_on(outputStream* st) const { - st->print("Shenandoah Concurrent Thread"); - Thread::print_on(st); - st->cr(); -} - void ShenandoahControlThread::start() { create_and_start(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp index 84ffa11e8f0..782b134a449 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.hpp @@ -142,12 +142,6 @@ class ShenandoahControlThread: public ConcurrentGCThread { void start(); void prepare_for_graceful_shutdown(); bool in_graceful_shutdown(); - - char* name() const { return (char*)"ShenandoahControlThread";} - - // Printing - void print_on(outputStream* st) const; - void print() const; }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHCONTROLTHREAD_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index 0b312858059..d39f97530cc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -475,8 +475,8 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) : _phase_timings(NULL), _monitoring_support(NULL), _memory_pool(NULL), - _stw_memory_manager("Shenandoah Pauses", "end of GC pause"), - _cycle_memory_manager("Shenandoah Cycles", "end of GC cycle"), + _stw_memory_manager("Shenandoah Pauses"), + _cycle_memory_manager("Shenandoah Cycles"), _gc_timer(new (ResourceObj::C_HEAP, mtGC) ConcurrentGCTimer()), _soft_ref_policy(), _log_min_obj_alignment_in_bytes(LogMinObjAlignmentInBytes), @@ -1182,6 +1182,7 @@ void ShenandoahHeap::prepare_for_verify() { } void ShenandoahHeap::gc_threads_do(ThreadClosure* tcl) const { + tcl->do_thread(_control_thread); workers()->threads_do(tcl); if (_safepoint_workers != NULL) { _safepoint_workers->threads_do(tcl); @@ -1740,20 +1741,8 @@ size_t ShenandoahHeap::tlab_used(Thread* thread) const { } bool ShenandoahHeap::try_cancel_gc() { - while (true) { - jbyte prev = _cancelled_gc.cmpxchg(CANCELLED, CANCELLABLE); - if (prev == CANCELLABLE) return true; - else if (prev == CANCELLED) return false; - assert(ShenandoahSuspendibleWorkers, "should not get here when not using suspendible workers"); - assert(prev == NOT_CANCELLED, "must be NOT_CANCELLED"); - Thread* thread = Thread::current(); - if (thread->is_Java_thread()) { - // We need to provide a safepoint here, otherwise we might - // spin forever if a SP is pending. - ThreadBlockInVM sp(thread->as_Java_thread()); - SpinPause(); - } - } + jbyte prev = _cancelled_gc.cmpxchg(CANCELLED, CANCELLABLE); + return prev == CANCELLABLE; } void ShenandoahHeap::cancel_gc(GCCause::Cause cause) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 12e863584b2..fcaa18ccbd6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -326,12 +326,7 @@ class ShenandoahHeap : public CollectedHeap { // GC has been cancelled. Worker threads can not suspend for // safepoint but must finish their work as soon as possible. - CANCELLED, - - // GC has not been cancelled and must not be cancelled. At least - // one worker thread checks for pending safepoint and may suspend - // if a safepoint is pending. - NOT_CANCELLED + CANCELLED }; ShenandoahSharedEnumFlag _cancelled_gc; @@ -635,9 +630,17 @@ class ShenandoahHeap : public CollectedHeap { template inline void update_with_forwarded(T* p); - static inline oop cas_oop(oop n, narrowOop* addr, oop c); - static inline oop cas_oop(oop n, oop* addr, oop c); - static inline oop cas_oop(oop n, narrowOop* addr, narrowOop c); + static inline void atomic_update_oop(oop update, oop* addr, oop compare); + static inline void atomic_update_oop(oop update, narrowOop* addr, oop compare); + static inline void atomic_update_oop(oop update, narrowOop* addr, narrowOop compare); + + static inline bool atomic_update_oop_check(oop update, oop* addr, oop compare); + static inline bool atomic_update_oop_check(oop update, narrowOop* addr, oop compare); + static inline bool atomic_update_oop_check(oop update, narrowOop* addr, narrowOop compare); + + static inline void atomic_clear_oop( oop* addr, oop compare); + static inline void atomic_clear_oop(narrowOop* addr, oop compare); + static inline void atomic_clear_oop(narrowOop* addr, narrowOop compare); void trash_humongous_region_at(ShenandoahHeapRegion *r); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index f300233d7e2..1bd3857b8b4 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -132,29 +132,110 @@ inline void ShenandoahHeap::conc_update_with_forwarded(T* p) { // Either we succeed in updating the reference, or something else gets in our way. // We don't care if that is another concurrent GC update, or another mutator update. - // We only check that non-NULL store still updated with non-forwarded reference. - oop witness = cas_oop(fwd, p, obj); - shenandoah_assert_not_forwarded_except(p, witness, (witness == NULL) || (witness == obj)); + atomic_update_oop(fwd, p, obj); } } } -inline oop ShenandoahHeap::cas_oop(oop n, oop* addr, oop c) { +// Atomic updates of heap location. This is only expected to work with updating the same +// logical object with its forwardee. The reason why we need stronger-than-relaxed memory +// ordering has to do with coordination with GC barriers and mutator accesses. +// +// In essence, stronger CAS access is required to maintain the transitive chains that mutator +// accesses build by themselves. To illustrate this point, consider the following example. +// +// Suppose "o" is the object that has a field "x" and the reference to "o" is stored +// to field at "addr", which happens to be Java volatile field. Normally, the accesses to volatile +// field at "addr" would be matched with release/acquire barriers. This changes when GC moves +// the object under mutator feet. +// +// Thread 1 (Java) +// // --- previous access starts here +// ... +// T1.1: store(&o.x, 1, mo_relaxed) +// T1.2: store(&addr, o, mo_release) // volatile store +// +// // --- new access starts here +// // LRB: copy and install the new copy to fwdptr +// T1.3: var copy = copy(o) +// T1.4: cas(&fwd, t, copy, mo_release) // pointer-mediated publication +// +// +// Thread 2 (GC updater) +// T2.1: var f = load(&fwd, mo_{consume|acquire}) // pointer-mediated acquisition +// T2.2: cas(&addr, o, f, mo_release) // this method +// +// Thread 3 (Java) +// T3.1: var o = load(&addr, mo_acquire) // volatile read +// T3.2: if (o != null) +// T3.3: var r = load(&o.x, mo_relaxed) +// +// r is guaranteed to contain "1". +// +// Without GC involvement, there is synchronizes-with edge from T1.2 to T3.1, +// which guarantees this. With GC involvement, when LRB copies the object and +// another thread updates the reference to it, we need to have the transitive edge +// from T1.4 to T2.1 (that one is guaranteed by forwarding accesses), plus the edge +// from T2.2 to T3.1 (which is brought by this CAS). +// +// Note that we do not need to "acquire" in these methods, because we do not read the +// failure witnesses contents on any path, and "release" is enough. +// + +inline void ShenandoahHeap::atomic_update_oop(oop update, oop* addr, oop compare) { assert(is_aligned(addr, HeapWordSize), "Address should be aligned: " PTR_FORMAT, p2i(addr)); - return (oop) Atomic::cmpxchg(addr, c, n); + Atomic::cmpxchg(addr, compare, update, memory_order_release); } -inline oop ShenandoahHeap::cas_oop(oop n, narrowOop* addr, narrowOop c) { +inline void ShenandoahHeap::atomic_update_oop(oop update, narrowOop* addr, narrowOop compare) { assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr)); - narrowOop val = CompressedOops::encode(n); - return CompressedOops::decode(Atomic::cmpxchg(addr, c, val)); + narrowOop u = CompressedOops::encode(update); + Atomic::cmpxchg(addr, compare, u, memory_order_release); } -inline oop ShenandoahHeap::cas_oop(oop n, narrowOop* addr, oop c) { +inline void ShenandoahHeap::atomic_update_oop(oop update, narrowOop* addr, oop compare) { assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr)); - narrowOop cmp = CompressedOops::encode(c); - narrowOop val = CompressedOops::encode(n); - return CompressedOops::decode(Atomic::cmpxchg(addr, cmp, val)); + narrowOop c = CompressedOops::encode(compare); + narrowOop u = CompressedOops::encode(update); + Atomic::cmpxchg(addr, c, u, memory_order_release); +} + +inline bool ShenandoahHeap::atomic_update_oop_check(oop update, oop* addr, oop compare) { + assert(is_aligned(addr, HeapWordSize), "Address should be aligned: " PTR_FORMAT, p2i(addr)); + return (oop) Atomic::cmpxchg(addr, compare, update, memory_order_release) == compare; +} + +inline bool ShenandoahHeap::atomic_update_oop_check(oop update, narrowOop* addr, narrowOop compare) { + assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr)); + narrowOop u = CompressedOops::encode(update); + return (narrowOop) Atomic::cmpxchg(addr, compare, u, memory_order_release) == compare; +} + +inline bool ShenandoahHeap::atomic_update_oop_check(oop update, narrowOop* addr, oop compare) { + assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr)); + narrowOop c = CompressedOops::encode(compare); + narrowOop u = CompressedOops::encode(update); + return CompressedOops::decode(Atomic::cmpxchg(addr, c, u, memory_order_release)) == compare; +} + +// The memory ordering discussion above does not apply for methods that store NULLs: +// then, there is no transitive reads in mutator (as we see NULLs), and we can do +// relaxed memory ordering there. + +inline void ShenandoahHeap::atomic_clear_oop(oop* addr, oop compare) { + assert(is_aligned(addr, HeapWordSize), "Address should be aligned: " PTR_FORMAT, p2i(addr)); + Atomic::cmpxchg(addr, compare, oop(), memory_order_relaxed); +} + +inline void ShenandoahHeap::atomic_clear_oop(narrowOop* addr, oop compare) { + assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr)); + narrowOop cmp = CompressedOops::encode(compare); + Atomic::cmpxchg(addr, cmp, narrowOop(), memory_order_relaxed); +} + +inline void ShenandoahHeap::atomic_clear_oop(narrowOop* addr, narrowOop compare) { + assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr)); + Atomic::cmpxchg(addr, compare, narrowOop(), memory_order_relaxed); } inline bool ShenandoahHeap::cancelled_gc() const { @@ -162,25 +243,12 @@ inline bool ShenandoahHeap::cancelled_gc() const { } inline bool ShenandoahHeap::check_cancelled_gc_and_yield(bool sts_active) { - if (! (sts_active && ShenandoahSuspendibleWorkers)) { - return cancelled_gc(); - } - - jbyte prev = _cancelled_gc.cmpxchg(NOT_CANCELLED, CANCELLABLE); - if (prev == CANCELLABLE || prev == NOT_CANCELLED) { + if (sts_active && ShenandoahSuspendibleWorkers && !cancelled_gc()) { if (SuspendibleThreadSet::should_yield()) { SuspendibleThreadSet::yield(); } - - // Back to CANCELLABLE. The thread that poked NOT_CANCELLED first gets - // to restore to CANCELLABLE. - if (prev == CANCELLABLE) { - _cancelled_gc.set(CANCELLABLE); - } - return false; - } else { - return true; } + return cancelled_gc(); } inline void ShenandoahHeap::clear_cancelled_gc() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp index 6ac55d60c80..e843f3b250b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp @@ -117,20 +117,9 @@ void reference_set_discovered(oop reference, oop discovered) { } template -static bool reference_cas_discovered(oop reference, oop discovered); - -template<> -bool reference_cas_discovered(oop reference, oop discovered) { - volatile narrowOop* addr = reinterpret_cast(java_lang_ref_Reference::discovered_addr_raw(reference)); - narrowOop compare = CompressedOops::encode(NULL); - narrowOop exchange = CompressedOops::encode(discovered); - return Atomic::cmpxchg(addr, compare, exchange) == compare; -} - -template<> -bool reference_cas_discovered(oop reference, oop discovered) { - volatile oop* addr = reinterpret_cast(java_lang_ref_Reference::discovered_addr_raw(reference)); - return Atomic::cmpxchg(addr, oop(NULL), discovered) == NULL; +static bool reference_cas_discovered(oop reference, oop discovered) { + T* addr = reinterpret_cast(java_lang_ref_Reference::discovered_addr_raw(reference)); + return ShenandoahHeap::atomic_update_oop_check(discovered, addr, NULL); } template diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp index 9a7213241b7..675bbe2f8d6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.cpp @@ -52,6 +52,7 @@ ShenandoahGCSession::ShenandoahGCSession(GCCause::Cause cause) : _heap->shenandoah_policy()->record_cycle_start(); _heap->heuristics()->record_cycle_start(); _trace_cycle.initialize(_heap->cycle_memory_manager(), cause, + "end of GC cycle", /* allMemoryPoolsAffected */ true, /* recordGCBeginTime = */ true, /* recordPreGCUsage = */ true, @@ -73,9 +74,10 @@ ShenandoahGCSession::~ShenandoahGCSession() { _heap->set_gc_cause(GCCause::_no_gc); } -ShenandoahGCPauseMark::ShenandoahGCPauseMark(uint gc_id, SvcGCMarker::reason_type type) : +ShenandoahGCPauseMark::ShenandoahGCPauseMark(uint gc_id, const char* notification_message, SvcGCMarker::reason_type type) : _heap(ShenandoahHeap::heap()), _gc_id_mark(gc_id), _svc_gc_mark(type), _is_gc_active_mark() { _trace_pause.initialize(_heap->stw_memory_manager(), _heap->gc_cause(), + notification_message, /* allMemoryPoolsAffected */ true, /* recordGCBeginTime = */ true, /* recordPreGCUsage = */ false, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp index 49ff6ca2864..f543800131c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp @@ -135,7 +135,7 @@ class ShenandoahGCPauseMark : public StackObj { TraceMemoryManagerStats _trace_pause; public: - ShenandoahGCPauseMark(uint gc_id, SvcGCMarker::reason_type type); + ShenandoahGCPauseMark(uint gc_id, const char* notification_action, SvcGCMarker::reason_type type); }; class ShenandoahSafepoint : public AllStatic { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp index 18bd4d6e6aa..d6be0920558 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVMOperations.cpp @@ -47,36 +47,36 @@ void VM_ShenandoahReferenceOperation::doit_epilogue() { } void VM_ShenandoahInitMark::doit() { - ShenandoahGCPauseMark mark(_gc_id, SvcGCMarker::CONCURRENT); + ShenandoahGCPauseMark mark(_gc_id, "Init Mark", SvcGCMarker::CONCURRENT); _gc->entry_init_mark(); } void VM_ShenandoahFinalMarkStartEvac::doit() { - ShenandoahGCPauseMark mark(_gc_id, SvcGCMarker::CONCURRENT); + ShenandoahGCPauseMark mark(_gc_id, "Final Mark", SvcGCMarker::CONCURRENT); _gc->entry_final_mark(); } void VM_ShenandoahFullGC::doit() { - ShenandoahGCPauseMark mark(_gc_id, SvcGCMarker::FULL); + ShenandoahGCPauseMark mark(_gc_id, "Full GC", SvcGCMarker::FULL); _full_gc->entry_full(_gc_cause); } void VM_ShenandoahDegeneratedGC::doit() { - ShenandoahGCPauseMark mark(_gc_id, SvcGCMarker::CONCURRENT); + ShenandoahGCPauseMark mark(_gc_id, "Degenerated GC", SvcGCMarker::CONCURRENT); _gc->entry_degenerated(); } void VM_ShenandoahInitUpdateRefs::doit() { - ShenandoahGCPauseMark mark(_gc_id, SvcGCMarker::CONCURRENT); + ShenandoahGCPauseMark mark(_gc_id, "Init Update Refs", SvcGCMarker::CONCURRENT); _gc->entry_init_updaterefs(); } void VM_ShenandoahFinalUpdateRefs::doit() { - ShenandoahGCPauseMark mark(_gc_id, SvcGCMarker::CONCURRENT); + ShenandoahGCPauseMark mark(_gc_id, "Final Update Refs", SvcGCMarker::CONCURRENT); _gc->entry_final_updaterefs(); } void VM_ShenandoahFinalRoots::doit() { - ShenandoahGCPauseMark mark(_gc_id, SvcGCMarker::CONCURRENT); + ShenandoahGCPauseMark mark(_gc_id, "Final Roots", SvcGCMarker::CONCURRENT); _gc->entry_final_roots(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index 5a920fb884b..88c460b5f19 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -334,7 +334,7 @@ "How many times to maximum attempt to flush SATB buffers at the " \ "end of concurrent marking.") \ \ - product(bool, ShenandoahSuspendibleWorkers, false, EXPERIMENTAL, \ + product(bool, ShenandoahSuspendibleWorkers, true, EXPERIMENTAL, \ "Suspend concurrent GC worker threads at safepoints") \ \ product(bool, ShenandoahSATBBarrier, true, DIAGNOSTIC, \ diff --git a/src/hotspot/share/gc/z/zServiceability.cpp b/src/hotspot/share/gc/z/zServiceability.cpp index 744ad7270e3..c708d0d0c47 100644 --- a/src/hotspot/share/gc/z/zServiceability.cpp +++ b/src/hotspot/share/gc/z/zServiceability.cpp @@ -110,9 +110,8 @@ MemoryUsage ZServiceabilityMemoryPool::get_memory_usage() { } ZServiceabilityMemoryManager::ZServiceabilityMemoryManager(const char* name, - const char* end_message, ZServiceabilityMemoryPool* pool) : - GCMemoryManager(name, end_message) { + GCMemoryManager(name) { add_pool(pool); } @@ -120,8 +119,8 @@ ZServiceability::ZServiceability(size_t min_capacity, size_t max_capacity) : _min_capacity(min_capacity), _max_capacity(max_capacity), _memory_pool(_min_capacity, _max_capacity), - _cycle_memory_manager("ZGC Cycles", "end of GC cycle", &_memory_pool), - _pause_memory_manager("ZGC Pauses", "end of GC pause", &_memory_pool), + _cycle_memory_manager("ZGC Cycles", &_memory_pool), + _pause_memory_manager("ZGC Pauses", &_memory_pool), _counters(NULL) {} void ZServiceability::initialize() { @@ -147,6 +146,7 @@ ZServiceabilityCounters* ZServiceability::counters() { ZServiceabilityCycleTracer::ZServiceabilityCycleTracer() : _memory_manager_stats(ZHeap::heap()->serviceability_cycle_memory_manager(), ZCollectedHeap::heap()->gc_cause(), + "end of GC cycle", true /* allMemoryPoolsAffected */, true /* recordGCBeginTime */, true /* recordPreGCUsage */, @@ -161,6 +161,7 @@ ZServiceabilityPauseTracer::ZServiceabilityPauseTracer() : _counters_stats(ZHeap::heap()->serviceability_counters()->collector_counters()), _memory_manager_stats(ZHeap::heap()->serviceability_pause_memory_manager(), ZCollectedHeap::heap()->gc_cause(), + "end of GC pause", true /* allMemoryPoolsAffected */, true /* recordGCBeginTime */, false /* recordPreGCUsage */, diff --git a/src/hotspot/share/gc/z/zServiceability.hpp b/src/hotspot/share/gc/z/zServiceability.hpp index 3947038c4ce..5d03184d244 100644 --- a/src/hotspot/share/gc/z/zServiceability.hpp +++ b/src/hotspot/share/gc/z/zServiceability.hpp @@ -44,7 +44,6 @@ class ZServiceabilityMemoryPool : public CollectedMemoryPool { class ZServiceabilityMemoryManager : public GCMemoryManager { public: ZServiceabilityMemoryManager(const char* name, - const char* end_message, ZServiceabilityMemoryPool* pool); }; diff --git a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp index 875e7f8e475..a21a4fa7d48 100644 --- a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp +++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp @@ -30,6 +30,7 @@ #include "jfr/jni/jfrJavaSupport.hpp" #include "jfr/recorder/jfrRecorder.hpp" #include "jfr/recorder/service/jfrOptionSet.hpp" +#include "jfr/support/jfrThreadLocal.hpp" #include "logging/log.hpp" #include "logging/logConfiguration.hpp" #include "logging/logMessage.hpp" @@ -294,12 +295,10 @@ static void initialize_dummy_descriptors(GrowableArray* array // we keep them in a thread local arena. The arena is reset between invocations. static THREAD_LOCAL Arena* dcmd_arena = NULL; -static void prepare_dcmd_string_arena() { - if (dcmd_arena == NULL) { - dcmd_arena = new (mtTracing) Arena(mtTracing); - } else { - dcmd_arena->destruct_contents(); // will grow on next allocation - } +static void prepare_dcmd_string_arena(JavaThread* jt) { + dcmd_arena = JfrThreadLocal::dcmd_arena(jt); + assert(dcmd_arena != nullptr, "invariant"); + dcmd_arena->destruct_contents(); // will grow on next allocation } static char* dcmd_arena_allocate(size_t size) { @@ -307,7 +306,7 @@ static char* dcmd_arena_allocate(size_t size) { return (char*)dcmd_arena->Amalloc(size); } -static const char* get_as_dcmd_arena_string(oop string, JavaThread* t) { +static const char* get_as_dcmd_arena_string(oop string) { char* str = NULL; const typeArrayOop value = java_lang_String::value(string); if (value != NULL) { @@ -328,7 +327,7 @@ static const char* read_string_field(oop argument, const char* field_name, TRAPS args.set_receiver(argument); JfrJavaSupport::get_field(&args, THREAD); const oop string_oop = result.get_oop(); - return string_oop != NULL ? get_as_dcmd_arena_string(string_oop, (JavaThread*)THREAD) : NULL; + return string_oop != NULL ? get_as_dcmd_arena_string(string_oop) : NULL; } static bool read_boolean_field(oop argument, const char* field_name, TRAPS) { @@ -377,7 +376,7 @@ GrowableArray* JfrDCmd::argument_info_array() const { assert(arguments->is_array(), "must be array"); const int num_arguments = arguments->length(); assert(num_arguments == _num_arguments, "invariant"); - prepare_dcmd_string_arena(); + prepare_dcmd_string_arena(thread); for (int i = 0; i < num_arguments; ++i) { DCmdArgumentInfo* const dai = create_info(arguments->obj_at(i), thread); assert(dai != NULL, "invariant"); diff --git a/src/hotspot/share/jfr/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml index 6d7e5cf6b3f..e30bfe2b826 100644 --- a/src/hotspot/share/jfr/metadata/metadata.xml +++ b/src/hotspot/share/jfr/metadata/metadata.xml @@ -709,7 +709,9 @@ - + @@ -717,14 +719,18 @@ - + - + diff --git a/src/hotspot/share/jfr/periodic/jfrOSInterface.cpp b/src/hotspot/share/jfr/periodic/jfrOSInterface.cpp index 85f6614ff5e..a0aa9154ff5 100644 --- a/src/hotspot/share/jfr/periodic/jfrOSInterface.cpp +++ b/src/hotspot/share/jfr/periodic/jfrOSInterface.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -253,6 +253,8 @@ const char* JfrOSInterface::virtualization_name() { VirtualizationType vrt = VM_Version::get_detected_virtualization(); if (vrt == XenHVM) { return "Xen hardware-assisted virtualization"; + } else if (vrt == XenPVHVM) { + return "Xen optimized paravirtualization"; } else if (vrt == KVM) { return "KVM virtualization"; } else if (vrt == VMWare) { diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp index 48b6d657ebc..1c4fabc75c3 100644 --- a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp +++ b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp @@ -34,6 +34,7 @@ #include "jfr/recorder/storage/jfrStorage.hpp" #include "jfr/support/jfrThreadLocal.hpp" #include "memory/allocation.inline.hpp" +#include "memory/arena.hpp" #include "runtime/os.hpp" #include "runtime/thread.inline.hpp" #include "utilities/sizes.hpp" @@ -46,6 +47,7 @@ JfrThreadLocal::JfrThreadLocal() : _load_barrier_buffer_epoch_0(NULL), _load_barrier_buffer_epoch_1(NULL), _stackframes(NULL), + _dcmd_arena(nullptr), _trace_id(JfrTraceId::assign_thread_id()), _thread(), _data_lost(0), @@ -142,6 +144,10 @@ void JfrThreadLocal::release(Thread* t) { _load_barrier_buffer_epoch_1->set_retired(); _load_barrier_buffer_epoch_1 = NULL; } + if (_dcmd_arena != nullptr) { + delete _dcmd_arena; + _dcmd_arena = nullptr; + } } void JfrThreadLocal::release(JfrThreadLocal* tl, Thread* t) { @@ -218,3 +224,15 @@ void JfrThreadLocal::include(Thread* t) { u4 JfrThreadLocal::stackdepth() const { return _stackdepth != 0 ? _stackdepth : (u4)JfrOptionSet::stackdepth(); } + +Arena* JfrThreadLocal::dcmd_arena(JavaThread* jt) { + assert(jt != nullptr, "invariant"); + JfrThreadLocal* tl = jt->jfr_thread_local(); + Arena* arena = tl->_dcmd_arena; + if (arena != nullptr) { + return arena; + } + arena = new (mtTracing) Arena(mtTracing); + tl->_dcmd_arena = arena; + return arena; +} diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp index 7e4a0819cf2..ab7d6170cdb 100644 --- a/src/hotspot/share/jfr/support/jfrThreadLocal.hpp +++ b/src/hotspot/share/jfr/support/jfrThreadLocal.hpp @@ -28,6 +28,7 @@ #include "jfr/utilities/jfrBlob.hpp" #include "jfr/utilities/jfrTypes.hpp" +class Arena; class JavaThread; class JfrBuffer; class JfrStackFrame; @@ -42,6 +43,7 @@ class JfrThreadLocal { JfrBuffer* _load_barrier_buffer_epoch_0; JfrBuffer* _load_barrier_buffer_epoch_1; mutable JfrStackFrame* _stackframes; + Arena* _dcmd_arena; mutable traceid _trace_id; JfrBlobHandle _thread; u8 _data_lost; @@ -219,6 +221,8 @@ class JfrThreadLocal { return _dead; } + static Arena* dcmd_arena(JavaThread* jt); + bool has_thread_blob() const; void set_thread_blob(const JfrBlobHandle& handle); const JfrBlobHandle& thread_blob() const; diff --git a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp index 1eeb48a9a5f..3c8abb36ef1 100644 --- a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp +++ b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp @@ -1147,7 +1147,9 @@ void CodeInstaller::site_Call(CodeBuffer& buffer, jint pc_offset, JVMCIObject si CodeInstaller::pd_relocate_JavaMethod(buffer, hotspot_method, pc_offset, JVMCI_CHECK); if (_next_call_type == INVOKESTATIC || _next_call_type == INVOKESPECIAL) { // Need a static call stub for transitions from compiled to interpreted. - CompiledStaticCall::emit_to_interp_stub(buffer, _instructions->start() + pc_offset); + if (CompiledStaticCall::emit_to_interp_stub(buffer, _instructions->start() + pc_offset) == nullptr) { + JVMCI_ERROR("could not emit to_interp stub - code cache is full"); + } } } diff --git a/src/hotspot/share/memory/allocation.cpp b/src/hotspot/share/memory/allocation.cpp index 241213d0b2c..89be338dadc 100644 --- a/src/hotspot/share/memory/allocation.cpp +++ b/src/hotspot/share/memory/allocation.cpp @@ -49,7 +49,7 @@ char* AllocateHeap(size_t size, char* AllocateHeap(size_t size, MEMFLAGS flags, AllocFailType alloc_failmode /* = AllocFailStrategy::EXIT_OOM*/) { - return AllocateHeap(size, flags, CALLER_PC); + return AllocateHeap(size, flags, CALLER_PC, alloc_failmode); } char* ReallocateHeap(char *old, diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index 2c42c013560..3f29a72a86d 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -590,8 +590,8 @@ ReservedSpace Metaspace::reserve_address_space_for_compressed_classes(size_t siz #if defined(AARCH64) || defined(PPC64) const size_t alignment = Metaspace::reserve_alignment(); - // AArch64: Try to align metaspace so that we can decode a compressed - // klass with a single MOVK instruction. We can do this iff the + // AArch64: Try to align metaspace class space so that we can decode a + // compressed klass with a single MOVK instruction. We can do this iff the // compressed class base is a multiple of 4G. // Additionally, above 32G, ensure the lower LogKlassAlignmentInBytes bits // of the upper 32-bits of the address are zero so we can handle a shift @@ -614,19 +614,39 @@ ReservedSpace Metaspace::reserve_address_space_for_compressed_classes(size_t siz { NULL, NULL, 0 } }; + // Calculate a list of all possible values for the starting address for the + // compressed class space. + ResourceMark rm; + GrowableArray
list(36); for (int i = 0; search_ranges[i].from != NULL; i ++) { address a = search_ranges[i].from; assert(CompressedKlassPointers::is_valid_base(a), "Sanity"); while (a < search_ranges[i].to) { - ReservedSpace rs(size, Metaspace::reserve_alignment(), - os::vm_page_size(), (char*)a); - if (rs.is_reserved()) { - assert(a == (address)rs.base(), "Sanity"); - return rs; - } + list.append(a); a += search_ranges[i].increment; } } + + int len = list.length(); + int r = 0; + if (!DumpSharedSpaces) { + // Starting from a random position in the list. If the address cannot be reserved + // (the OS already assigned it for something else), go to the next position, wrapping + // around if necessary, until we exhaust all the items. + os::init_random((int)os::javaTimeNanos()); + r = os::random(); + log_info(metaspace)("Randomizing compressed class space: start from %d out of %d locations", + r % len, len); + } + for (int i = 0; i < len; i++) { + address a = list.at((i + r) % len); + ReservedSpace rs(size, Metaspace::reserve_alignment(), + os::vm_page_size(), (char*)a); + if (rs.is_reserved()) { + assert(a == (address)rs.base(), "Sanity"); + return rs; + } + } #endif // defined(AARCH64) || defined(PPC64) #ifdef AARCH64 diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index bf9416915ce..5c71a49c30b 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -1035,18 +1035,18 @@ void InstanceKlass::add_initialization_error(JavaThread* current, Handle excepti // If the initialization error is OOM, this might not work, but if GC kicks in // this would be still be helpful. JavaThread* THREAD = current; - Handle cause = java_lang_Throwable::get_cause_with_stack_trace(exception, THREAD); - if (HAS_PENDING_EXCEPTION || cause.is_null()) { - CLEAR_PENDING_EXCEPTION; + Handle init_error = java_lang_Throwable::create_initialization_error(current, exception); + ResourceMark rm(THREAD); + if (init_error.is_null()) { + log_trace(class, init)("Initialization error is null for class %s", external_name()); return; } MutexLocker ml(THREAD, ClassInitError_lock); - OopHandle elem = OopHandle(Universe::vm_global(), cause()); - bool created = false; + OopHandle elem = OopHandle(Universe::vm_global(), init_error()); + bool created; _initialization_error_table.put_if_absent(this, elem, &created); assert(created, "Initialization is single threaded"); - ResourceMark rm(THREAD); log_trace(class, init)("Initialization error added for class %s", external_name()); } diff --git a/src/hotspot/share/oops/symbol.cpp b/src/hotspot/share/oops/symbol.cpp index 3884c79f290..581a5f8f6dd 100644 --- a/src/hotspot/share/oops/symbol.cpp +++ b/src/hotspot/share/oops/symbol.cpp @@ -310,10 +310,8 @@ bool Symbol::try_increment_refcount() { // this caller. void Symbol::increment_refcount() { if (!try_increment_refcount()) { -#ifdef ASSERT print(); fatal("refcount has gone to zero"); -#endif } #ifndef PRODUCT if (refcount() != PERM_REFCOUNT) { // not a permanent symbol @@ -333,10 +331,8 @@ void Symbol::decrement_refcount() { if (refc == PERM_REFCOUNT) { return; // refcount is permanent, permanent is sticky } else if (refc == 0) { -#ifdef ASSERT print(); fatal("refcount underflow"); -#endif return; } else { found = Atomic::cmpxchg(&_hash_and_refcount, old_value, old_value - 1); @@ -356,10 +352,8 @@ void Symbol::make_permanent() { if (refc == PERM_REFCOUNT) { return; // refcount is permanent, permanent is sticky } else if (refc == 0) { -#ifdef ASSERT print(); fatal("refcount underflow"); -#endif return; } else { int hash = extract_hash(old_value); diff --git a/src/hotspot/share/opto/block.cpp b/src/hotspot/share/opto/block.cpp index 648ac5c671b..c649786872b 100644 --- a/src/hotspot/share/opto/block.cpp +++ b/src/hotspot/share/opto/block.cpp @@ -1378,7 +1378,13 @@ void PhaseCFG::verify() const { } } } - assert(is_loop || block->find_node(def) < j, "uses must follow definitions"); + // Uses must be before definition, except if: + // - We are in some kind of loop we already detected + // - We are in infinite loop, where Region may not have been turned into LoopNode + assert(block->find_node(def) < j || + is_loop || + (n->is_Phi() && block->head()->as_Region()->is_in_infinite_subgraph()), + "uses must follow definitions (except in loops)"); } } } diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index f939145e198..1760f9672b3 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -601,6 +601,7 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt case vmIntrinsics::_putLongUnaligned: case vmIntrinsics::_loadFence: case vmIntrinsics::_storeFence: + case vmIntrinsics::_storeStoreFence: case vmIntrinsics::_fullFence: case vmIntrinsics::_currentThread: #ifdef JFR_HAVE_INTRINSICS diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index fc0d741945c..a6d50b44770 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -1654,6 +1654,7 @@ AllocateNode::AllocateNode(Compile* C, const TypeFunc *atype, init_req( KlassNode , klass_node); init_req( InitialTest , initial_test); init_req( ALength , topnode); + init_req( ValidLengthTest , topnode); C->add_macro_node(this); } @@ -1686,54 +1687,6 @@ Node *AllocateNode::make_ideal_mark(PhaseGVN *phase, Node* obj, Node* control, N return mark_node; } -//============================================================================= -Node* AllocateArrayNode::Ideal(PhaseGVN *phase, bool can_reshape) { - if (remove_dead_region(phase, can_reshape)) return this; - // Don't bother trying to transform a dead node - if (in(0) && in(0)->is_top()) return NULL; - - const Type* type = phase->type(Ideal_length()); - if (type->isa_int() && type->is_int()->_hi < 0) { - if (can_reshape) { - PhaseIterGVN *igvn = phase->is_IterGVN(); - // Unreachable fall through path (negative array length), - // the allocation can only throw so disconnect it. - Node* proj = proj_out_or_null(TypeFunc::Control); - Node* catchproj = NULL; - if (proj != NULL) { - for (DUIterator_Fast imax, i = proj->fast_outs(imax); i < imax; i++) { - Node *cn = proj->fast_out(i); - if (cn->is_Catch()) { - catchproj = cn->as_Multi()->proj_out_or_null(CatchProjNode::fall_through_index); - break; - } - } - } - if (catchproj != NULL && catchproj->outcnt() > 0 && - (catchproj->outcnt() > 1 || - catchproj->unique_out()->Opcode() != Op_Halt)) { - assert(catchproj->is_CatchProj(), "must be a CatchProjNode"); - Node* nproj = catchproj->clone(); - igvn->register_new_node_with_optimizer(nproj); - - Node *frame = new ParmNode( phase->C->start(), TypeFunc::FramePtr ); - frame = phase->transform(frame); - // Halt & Catch Fire - Node* halt = new HaltNode(nproj, frame, "unexpected negative array length"); - phase->C->root()->add_req(halt); - phase->transform(halt); - - igvn->replace_node(catchproj, phase->C->top()); - return this; - } - } else { - // Can't correct it during regular GVN so register for IGVN - phase->C->record_for_igvn(this); - } - } - return NULL; -} - // Retrieve the length from the AllocateArrayNode. Narrow the type with a // CastII, if appropriate. If we are not allowed to create new nodes, and // a CastII is appropriate, return NULL. diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index 7c8baefbca9..1017a76a25e 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -913,6 +913,7 @@ class AllocateNode : public CallNode { KlassNode, // type (maybe dynamic) of the obj. InitialTest, // slow-path test (may be constant) ALength, // array length (or TOP if none) + ValidLengthTest, ParmLimit }; @@ -922,6 +923,7 @@ class AllocateNode : public CallNode { fields[KlassNode] = TypeInstPtr::NOTNULL; fields[InitialTest] = TypeInt::BOOL; fields[ALength] = t; // length (can be a bad length) + fields[ValidLengthTest] = TypeInt::BOOL; const TypeTuple *domain = TypeTuple::make(ParmLimit, fields); @@ -1016,18 +1018,16 @@ class AllocateNode : public CallNode { // class AllocateArrayNode : public AllocateNode { public: - AllocateArrayNode(Compile* C, const TypeFunc *atype, Node *ctrl, Node *mem, Node *abio, - Node* size, Node* klass_node, Node* initial_test, - Node* count_val - ) + AllocateArrayNode(Compile* C, const TypeFunc* atype, Node* ctrl, Node* mem, Node* abio, Node* size, Node* klass_node, + Node* initial_test, Node* count_val, Node* valid_length_test) : AllocateNode(C, atype, ctrl, mem, abio, size, klass_node, initial_test) { init_class_id(Class_AllocateArray); set_req(AllocateNode::ALength, count_val); + set_req(AllocateNode::ValidLengthTest, valid_length_test); } virtual int Opcode() const; - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); // Dig the length operand out of a array allocation site. Node* Ideal_length() { diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 50de630681c..044a153efd6 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -39,6 +39,7 @@ #include "opto/narrowptrnode.hpp" #include "opto/mulnode.hpp" #include "opto/phaseX.hpp" +#include "opto/regalloc.hpp" #include "opto/regmask.hpp" #include "opto/runtime.hpp" #include "opto/subnode.hpp" @@ -393,6 +394,47 @@ bool RegionNode::is_unreachable_from_root(const PhaseGVN* phase) const { return true; // The Region node is unreachable - it is dead. } +#ifdef ASSERT +// Is this region in an infinite subgraph? +// (no path to root except through false NeverBranch exit) +bool RegionNode::is_in_infinite_subgraph() { + ResourceMark rm; + Unique_Node_List worklist; + worklist.push(this); + return RegionNode::are_all_nodes_in_infinite_subgraph(worklist); +} + +// Are all nodes in worklist in infinite subgraph? +// (no path to root except through false NeverBranch exit) +// worklist is directly used for the traversal +bool RegionNode::are_all_nodes_in_infinite_subgraph(Unique_Node_List& worklist) { + // BFS traversal down the CFG, except through NeverBranch exits + for (uint i = 0; i < worklist.size(); ++i) { + Node* n = worklist.at(i); + assert(n->is_CFG(), "only traverse CFG"); + if (n->is_Root()) { + // Found root -> there was an exit! + return false; + } else if (n->is_NeverBranch()) { + // Only follow the loop-internal projection, not the NeverBranch exit + ProjNode* proj = n->as_NeverBranch()->proj_out_or_null(0); + assert(proj != nullptr, "must find loop-internal projection of NeverBranch"); + worklist.push(proj); + } else { + // Traverse all CFG outputs + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* use = n->fast_out(i); + if (use->is_CFG()) { + worklist.push(use); + } + } + } + } + // No exit found for any loop -> all are infinite + return true; +} +#endif //ASSERT + bool RegionNode::try_clean_mem_phi(PhaseGVN *phase) { // Incremental inlining + PhaseStringOpts sometimes produce: // @@ -2639,6 +2681,17 @@ const Type* CatchNode::Value(PhaseGVN* phase) const { // Rethrows always throw exceptions, never return if (call->entry_point() == OptoRuntime::rethrow_stub()) { f[CatchProjNode::fall_through_index] = Type::TOP; + } else if (call->is_AllocateArray()) { + Node* klass_node = call->in(AllocateNode::KlassNode); + Node* length = call->in(AllocateNode::ALength); + const Type* length_type = phase->type(length); + const Type* klass_type = phase->type(klass_node); + Node* valid_length_test = call->in(AllocateNode::ValidLengthTest); + const Type* valid_length_test_t = phase->type(valid_length_test); + if (length_type == Type::TOP || klass_type == Type::TOP || valid_length_test_t == Type::TOP || + valid_length_test_t->is_int()->is_con(0)) { + f[CatchProjNode::fall_through_index] = Type::TOP; + } } else if( call->req() > TypeFunc::Parms ) { const Type *arg0 = phase->type( call->in(TypeFunc::Parms) ); // Check for null receiver to virtual or interface calls @@ -2749,3 +2802,25 @@ void NeverBranchNode::format( PhaseRegAlloc *ra_, outputStream *st) const { st->print("%s", Name()); } #endif + +#ifndef PRODUCT +void BlackholeNode::format(PhaseRegAlloc* ra, outputStream* st) const { + st->print("blackhole "); + bool first = true; + for (uint i = 0; i < req(); i++) { + Node* n = in(i); + if (n != NULL && OptoReg::is_valid(ra->get_reg_first(n))) { + if (first) { + first = false; + } else { + st->print(", "); + } + char buf[128]; + ra->dump_register(n, buf); + st->print("%s", buf); + } + } + st->cr(); +} +#endif + diff --git a/src/hotspot/share/opto/cfgnode.hpp b/src/hotspot/share/opto/cfgnode.hpp index 0f754aee453..d895d25db52 100644 --- a/src/hotspot/share/opto/cfgnode.hpp +++ b/src/hotspot/share/opto/cfgnode.hpp @@ -47,6 +47,7 @@ class PCTableNode; class JumpNode; class CatchNode; class NeverBranchNode; +class BlackholeNode; class ProjNode; class CProjNode; class IfTrueNode; @@ -90,6 +91,10 @@ class RegionNode : public Node { PhiNode* has_unique_phi() const; // returns the unique phi user, or NULL // Is this region node unreachable from root? bool is_unreachable_region(const PhaseGVN* phase); +#ifdef ASSERT + bool is_in_infinite_subgraph(); + static bool are_all_nodes_in_infinite_subgraph(Unique_Node_List& worklist); +#endif //ASSERT virtual int Opcode() const; virtual uint size_of() const { return sizeof(*this); } virtual bool pinned() const { return (const Node*)in(0) == this; } @@ -619,4 +624,28 @@ class NeverBranchNode : public MultiBranchNode { #endif }; +//------------------------------BlackholeNode---------------------------- +// Blackhole all arguments. This node would survive through the compiler +// the effects on its arguments, and would be finally matched to nothing. +class BlackholeNode : public MultiNode { +public: + BlackholeNode(Node* ctrl) : MultiNode(1) { + init_req(TypeFunc::Control, ctrl); + } + virtual int Opcode() const; + virtual uint ideal_reg() const { return 0; } // not matched in the AD file + virtual const Type* bottom_type() const { return TypeTuple::MEMBAR; } + + const RegMask &in_RegMask(uint idx) const { + // Fake the incoming arguments mask for blackholes: accept all registers + // and all stack slots. This would avoid any redundant register moves + // for blackhole inputs. + return RegMask::All; + } +#ifndef PRODUCT + virtual void format(PhaseRegAlloc* ra, outputStream* st) const; +#endif +}; + + #endif // SHARE_OPTO_CFGNODE_HPP diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 582ec51fbde..614d0b4e112 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -219,6 +219,7 @@ macro(MemBarAcquireLock) macro(MemBarCPUOrder) macro(MemBarRelease) macro(StoreFence) +macro(StoreStoreFence) macro(MemBarReleaseLock) macro(MemBarVolatile) macro(MemBarStoreStore) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index e3de3affbc1..fddae5d0e13 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -953,6 +953,12 @@ void Compile::Init(int aliaslevel) { _immutable_memory = NULL; // filled in at first inquiry +#ifdef ASSERT + _type_verify_symmetry = true; + _phase_optimize_finished = false; + _exception_backedge = false; +#endif + // Globally visible Nodes // First set TOP to NULL to give safe behavior during creation of RootNode set_cached_top_node(NULL); @@ -1055,12 +1061,6 @@ void Compile::Init(int aliaslevel) { Copy::zero_to_bytes(_alias_cache, sizeof(_alias_cache)); // A NULL adr_type hits in the cache right away. Preload the right answer. probe_alias_cache(NULL)->_index = AliasIdxTop; - -#ifdef ASSERT - _type_verify_symmetry = true; - _phase_optimize_finished = false; - _exception_backedge = false; -#endif } //---------------------------init_start---------------------------------------- @@ -2871,7 +2871,7 @@ void Compile::eliminate_redundant_card_marks(Node* n) { //------------------------------final_graph_reshaping_impl---------------------- // Implement items 1-5 from final_graph_reshaping below. -void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { +void Compile::final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes) { if ( n->outcnt() == 0 ) return; // dead node uint nop = n->Opcode(); @@ -2922,9 +2922,9 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { } #endif // Count FPU ops and common calls, implements item (3) - bool gc_handled = BarrierSet::barrier_set()->barrier_set_c2()->final_graph_reshaping(this, n, nop); + bool gc_handled = BarrierSet::barrier_set()->barrier_set_c2()->final_graph_reshaping(this, n, nop, dead_nodes); if (!gc_handled) { - final_graph_reshaping_main_switch(n, frc, nop); + final_graph_reshaping_main_switch(n, frc, nop, dead_nodes); } // Collect CFG split points @@ -2933,7 +2933,7 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { } } -void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop) { +void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes) { switch( nop ) { // Count all float operations that may use FPU case Op_AddF: @@ -3518,22 +3518,8 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f // that input may be a chain of Phis. If those phis have no // other use, then the MemBarAcquire keeps them alive and // register allocation can be confused. - ResourceMark rm; - Unique_Node_List wq; - wq.push(n->in(MemBarNode::Precedent)); + dead_nodes.push(n->in(MemBarNode::Precedent)); n->set_req(MemBarNode::Precedent, top()); - while (wq.size() > 0) { - Node* m = wq.pop(); - if (m->outcnt() == 0 && m != top()) { - for (uint j = 0; j < m->req(); j++) { - Node* in = m->in(j); - if (in != NULL) { - wq.push(in); - } - } - m->disconnect_inputs(this); - } - } } break; } @@ -3606,7 +3592,7 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f //------------------------------final_graph_reshaping_walk--------------------- // Replacing Opaque nodes with their input in final_graph_reshaping_impl(), // requires that the walk visits a node's inputs before visiting the node. -void Compile::final_graph_reshaping_walk( Node_Stack &nstack, Node *root, Final_Reshape_Counts &frc ) { +void Compile::final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes) { Unique_Node_List sfpt; frc._visited.set(root->_idx); // first, mark node as visited @@ -3632,7 +3618,7 @@ void Compile::final_graph_reshaping_walk( Node_Stack &nstack, Node *root, Final_ } } else { // Now do post-visit work - final_graph_reshaping_impl( n, frc ); + final_graph_reshaping_impl(n, frc, dead_nodes); if (nstack.is_empty()) break; // finished n = nstack.node(); // Get node from stack @@ -3732,7 +3718,8 @@ bool Compile::final_graph_reshaping() { // Visit everybody reachable! // Allocate stack of size C->live_nodes()/2 to avoid frequent realloc Node_Stack nstack(live_nodes() >> 1); - final_graph_reshaping_walk(nstack, root(), frc); + Unique_Node_List dead_nodes; + final_graph_reshaping_walk(nstack, root(), frc, dead_nodes); // Check for unreachable (from below) code (i.e., infinite loops). for( uint i = 0; i < frc._tests.size(); i++ ) { @@ -3745,7 +3732,7 @@ bool Compile::final_graph_reshaping() { // 'fall-thru' path, so expected kids is 1 less. if (n->is_PCTable() && n->in(0) && n->in(0)->in(0)) { if (n->in(0)->in(0)->is_Call()) { - CallNode *call = n->in(0)->in(0)->as_Call(); + CallNode* call = n->in(0)->in(0)->as_Call(); if (call->entry_point() == OptoRuntime::rethrow_stub()) { required_outcnt--; // Rethrow always has 1 less kid } else if (call->req() > TypeFunc::Parms && @@ -3754,22 +3741,27 @@ bool Compile::final_graph_reshaping() { // detected that the virtual call will always result in a null // pointer exception. The fall-through projection of this CatchNode // will not be populated. - Node *arg0 = call->in(TypeFunc::Parms); + Node* arg0 = call->in(TypeFunc::Parms); if (arg0->is_Type() && arg0->as_Type()->type()->higher_equal(TypePtr::NULL_PTR)) { required_outcnt--; } - } else if (call->entry_point() == OptoRuntime::new_array_Java() && - call->req() > TypeFunc::Parms+1 && - call->is_CallStaticJava()) { - // Check for negative array length. In such case, the optimizer has + } else if (call->entry_point() == OptoRuntime::new_array_Java() || + call->entry_point() == OptoRuntime::new_array_nozero_Java()) { + // Check for illegal array length. In such case, the optimizer has // detected that the allocation attempt will always result in an // exception. There is no fall-through projection of this CatchNode . - Node *arg1 = call->in(TypeFunc::Parms+1); - if (arg1->is_Type() && - arg1->as_Type()->type()->join(TypeInt::POS)->empty()) { + assert(call->is_CallStaticJava(), "static call expected"); + assert(call->req() == call->jvms()->endoff() + 1, "missing extra input"); + uint valid_length_test_input = call->req() - 1; + Node* valid_length_test = call->in(valid_length_test_input); + call->del_req(valid_length_test_input); + if (valid_length_test->find_int_con(1) == 0) { required_outcnt--; } + dead_nodes.push(valid_length_test); + assert(n->outcnt() == required_outcnt, "malformed control flow"); + continue; } } } @@ -3778,6 +3770,16 @@ bool Compile::final_graph_reshaping() { record_method_not_compilable("malformed control flow"); return true; // Not all targets reachable! } + } else if (n->is_PCTable() && n->in(0) && n->in(0)->in(0) && n->in(0)->in(0)->is_Call()) { + CallNode* call = n->in(0)->in(0)->as_Call(); + if (call->entry_point() == OptoRuntime::new_array_Java() || + call->entry_point() == OptoRuntime::new_array_nozero_Java()) { + assert(call->is_CallStaticJava(), "static call expected"); + assert(call->req() == call->jvms()->endoff() + 1, "missing extra input"); + uint valid_length_test_input = call->req() - 1; + dead_nodes.push(call->in(valid_length_test_input)); + call->del_req(valid_length_test_input); // valid length test useless now + } } // Check that I actually visited all kids. Unreached kids // must be infinite loops. @@ -3796,6 +3798,19 @@ bool Compile::final_graph_reshaping() { } } + while (dead_nodes.size() > 0) { + Node* m = dead_nodes.pop(); + if (m->outcnt() == 0 && m != top()) { + for (uint j = 0; j < m->req(); j++) { + Node* in = m->in(j); + if (in != NULL) { + dead_nodes.push(in); + } + } + m->disconnect_inputs(this); + } + } + #ifdef IA32 // If original bytecodes contained a mixture of floats and doubles // check if the optimizer has made it homogenous, item (3). diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 6e5f2ed2305..9f3817f3744 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -921,7 +921,8 @@ class Compile : public Phase { // Parsing, optimization PhaseGVN* initial_gvn() { return _initial_gvn; } Unique_Node_List* for_igvn() { return _for_igvn; } - inline void record_for_igvn(Node* n); // Body is after class Unique_Node_List. + inline void record_for_igvn(Node* n); // Body is after class Unique_Node_List in node.hpp. + inline void remove_for_igvn(Node* n); // Body is after class Unique_Node_List in node.hpp. void set_initial_gvn(PhaseGVN *gvn) { _initial_gvn = gvn; } void set_for_igvn(Unique_Node_List *for_igvn) { _for_igvn = for_igvn; } @@ -1113,9 +1114,9 @@ class Compile : public Phase { #endif // Function calls made by the public function final_graph_reshaping. // No need to be made public as they are not called elsewhere. - void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc); - void final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop); - void final_graph_reshaping_walk( Node_Stack &nstack, Node *root, Final_Reshape_Counts &frc ); + void final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes); + void final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes); + void final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes); void eliminate_redundant_card_marks(Node* n); // Logic cone optimization. diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index a3df43c2381..b9e155c143e 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -738,6 +738,29 @@ SafePointNode* GraphKit::clone_map() { return clonemap; } +//-----------------------------destruct_map_clone------------------------------ +// +// Order of destruct is important to increase the likelyhood that memory can be re-used. We need +// to destruct/free/delete in the exact opposite order as clone_map(). +void GraphKit::destruct_map_clone(SafePointNode* sfp) { + if (sfp == nullptr) return; + + Node* mem = sfp->memory(); + JVMState* jvms = sfp->jvms(); + + if (jvms != nullptr) { + delete jvms; + } + + remove_for_igvn(sfp); + gvn().clear_type(sfp); + sfp->destruct(&_gvn); + + if (mem != nullptr) { + gvn().clear_type(mem); + mem->destruct(&_gvn); + } +} //-----------------------------set_map_clone----------------------------------- void GraphKit::set_map_clone(SafePointNode* m) { @@ -2730,7 +2753,9 @@ void GraphKit::make_slow_call_ex(Node* call, ciInstanceKlass* ex_klass, bool sep // Make a catch node with just two handlers: fall-through and catch-all Node* i_o = _gvn.transform( new ProjNode(call, TypeFunc::I_O, separate_io_proj) ); Node* catc = _gvn.transform( new CatchNode(control(), i_o, 2) ); - Node* norm = _gvn.transform( new CatchProjNode(catc, CatchProjNode::fall_through_index, CatchProjNode::no_handler_bci) ); + Node* norm = new CatchProjNode(catc, CatchProjNode::fall_through_index, CatchProjNode::no_handler_bci); + _gvn.set_type_bottom(norm); + C->record_for_igvn(norm); Node* excp = _gvn.transform( new CatchProjNode(catc, CatchProjNode::catch_all_index, CatchProjNode::no_handler_bci) ); { PreserveJVMState pjvms(this); @@ -3969,20 +3994,28 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable) initial_slow_test = initial_slow_test->as_Bool()->as_int_value(&_gvn); } + const TypeOopPtr* ary_type = _gvn.type(klass_node)->is_klassptr()->as_instance_type(); + Node* valid_length_test = _gvn.intcon(1); + if (ary_type->isa_aryptr()) { + BasicType bt = ary_type->isa_aryptr()->elem()->array_element_basic_type(); + jint max = TypeAryPtr::max_array_length(bt); + Node* valid_length_cmp = _gvn.transform(new CmpUNode(length, intcon(max))); + valid_length_test = _gvn.transform(new BoolNode(valid_length_cmp, BoolTest::le)); + } + // Create the AllocateArrayNode and its result projections AllocateArrayNode* alloc = new AllocateArrayNode(C, AllocateArrayNode::alloc_type(TypeInt::INT), control(), mem, i_o(), size, klass_node, initial_slow_test, - length); + length, valid_length_test); // Cast to correct type. Note that the klass_node may be constant or not, // and in the latter case the actual array type will be inexact also. // (This happens via a non-constant argument to inline_native_newArray.) // In any case, the value of klass_node provides the desired array type. const TypeInt* length_type = _gvn.find_int_type(length); - const TypeOopPtr* ary_type = _gvn.type(klass_node)->is_klassptr()->as_instance_type(); if (ary_type->isa_aryptr() && length_type != NULL) { // Try to get a better type than POS for the size ary_type = ary_type->is_aryptr()->cast_to_size(length_type); diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index d815e21956f..6c87f3ee342 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -94,6 +94,7 @@ class GraphKit : public Phase { void* barrier_set_state() const { return C->barrier_set_state(); } void record_for_igvn(Node* n) const { C->record_for_igvn(n); } // delegate to Compile + void remove_for_igvn(Node* n) const { C->remove_for_igvn(n); } // Handy well-known nodes: Node* null() const { return zerocon(T_OBJECT); } @@ -170,6 +171,11 @@ class GraphKit : public Phase { // Clone the existing map state. (Implements PreserveJVMState.) SafePointNode* clone_map(); + // Reverses the work done by clone_map(). Should only be used when the node returned by + // clone_map() is ultimately not used. Calling Node::destruct directly in the previously + // mentioned circumstance instead of this method may result in use-after-free. + void destruct_map_clone(SafePointNode* sfp); + // Set the map to a clone of the given one. void set_map_clone(SafePointNode* m); diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index b5970545c4e..ea483750181 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -466,6 +466,7 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_loadFence: case vmIntrinsics::_storeFence: + case vmIntrinsics::_storeStoreFence: case vmIntrinsics::_fullFence: return inline_unsafe_fence(intrinsic_id()); case vmIntrinsics::_onSpinWait: return inline_onspinwait(); @@ -1219,7 +1220,7 @@ bool LibraryCallKit::inline_string_indexOfChar(StrIntrinsicNode::ArgEnc ae) { } assert(callee()->signature()->size() == 4, "String.indexOfChar() has 4 arguments"); Node* src = argument(0); // byte[] - Node* tgt = argument(1); // tgt is int ch + Node* int_ch = argument(1); Node* from_index = argument(2); Node* max = argument(3); @@ -1231,6 +1232,15 @@ bool LibraryCallKit::inline_string_indexOfChar(StrIntrinsicNode::ArgEnc ae) { // Range checks generate_string_range_check(src, src_offset, src_count, ae == StrIntrinsicNode::U); + + // Check for int_ch >= 0 + Node* int_ch_cmp = _gvn.transform(new CmpINode(int_ch, intcon(0))); + Node* int_ch_bol = _gvn.transform(new BoolNode(int_ch_cmp, BoolTest::ge)); + { + BuildCutout unless(this, int_ch_bol, PROB_MAX); + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_maybe_recompile); + } if (stopped()) { return true; } @@ -1238,7 +1248,7 @@ bool LibraryCallKit::inline_string_indexOfChar(StrIntrinsicNode::ArgEnc ae) { RegionNode* region = new RegionNode(3); Node* phi = new PhiNode(region, TypeInt::INT); - Node* result = new StrIndexOfCharNode(control(), memory(TypeAryPtr::BYTES), src_start, src_count, tgt, ae); + Node* result = new StrIndexOfCharNode(control(), memory(TypeAryPtr::BYTES), src_start, src_count, int_ch, ae); C->set_has_split_ifs(true); // Has chance for split-if optimization _gvn.transform(result); @@ -1287,10 +1297,13 @@ bool LibraryCallKit::inline_string_copy(bool compress) { AllocateArrayNode* alloc = tightly_coupled_allocation(dst); // Figure out the size and type of the elements we will be copying. - const Type* src_type = src->Value(&_gvn); - const Type* dst_type = dst->Value(&_gvn); - BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType dst_elem = dst_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + const TypeAryPtr* src_type = src->Value(&_gvn)->isa_aryptr(); + const TypeAryPtr* dst_type = dst->Value(&_gvn)->isa_aryptr(); + if (src_type == nullptr || dst_type == nullptr) { + return false; + } + BasicType src_elem = src_type->klass()->as_array_klass()->element_type()->basic_type(); + BasicType dst_elem = dst_type->klass()->as_array_klass()->element_type()->basic_type(); assert((compress && dst_elem == T_BYTE && (src_elem == T_BYTE || src_elem == T_CHAR)) || (!compress && src_elem == T_BYTE && (dst_elem == T_BYTE || dst_elem == T_CHAR)), "Unsupported array types for inline_string_copy"); @@ -1563,7 +1576,7 @@ bool LibraryCallKit::inline_string_char_access(bool is_store) { set_sp(old_sp); return false; } - old_map->destruct(&_gvn); + destruct_map_clone(old_map); if (is_store) { access_store_at(value, adr, TypeAryPtr::BYTES, ch, TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED); } else { @@ -2346,7 +2359,7 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c mismatched = true; // conservatively mark all "wide" on-heap accesses as mismatched } - old_map->destruct(&_gvn); + destruct_map_clone(old_map); assert(!mismatched || alias_type->adr_type()->is_oopptr(), "off-heap access can't be mismatched"); if (mismatched) { @@ -2597,7 +2610,7 @@ bool LibraryCallKit::inline_unsafe_load_store(const BasicType type, const LoadSt return false; } - old_map->destruct(&_gvn); + destruct_map_clone(old_map); // For CAS, unlike inline_unsafe_access, there seems no point in // trying to refine types. Just use the coarse types here. @@ -2690,6 +2703,9 @@ bool LibraryCallKit::inline_unsafe_fence(vmIntrinsics::ID id) { case vmIntrinsics::_storeFence: insert_mem_bar(Op_StoreFence); return true; + case vmIntrinsics::_storeStoreFence: + insert_mem_bar(Op_StoreStoreFence); + return true; case vmIntrinsics::_fullFence: insert_mem_bar(Op_MemBarVolatile); return true; @@ -4395,24 +4411,7 @@ JVMState* LibraryCallKit::arraycopy_restore_alloc_state(AllocateArrayNode* alloc } if (no_interfering_store) { - JVMState* old_jvms = alloc->jvms()->clone_shallow(C); - uint size = alloc->req(); - SafePointNode* sfpt = new SafePointNode(size, old_jvms); - old_jvms->set_map(sfpt); - for (uint i = 0; i < size; i++) { - sfpt->init_req(i, alloc->in(i)); - } - // re-push array length for deoptimization - sfpt->ins_req(old_jvms->stkoff() + old_jvms->sp(), alloc->in(AllocateNode::ALength)); - old_jvms->set_sp(old_jvms->sp()+1); - old_jvms->set_monoff(old_jvms->monoff()+1); - old_jvms->set_scloff(old_jvms->scloff()+1); - old_jvms->set_endoff(old_jvms->endoff()+1); - old_jvms->set_should_reexecute(true); - - sfpt->set_i_o(map()->i_o()); - sfpt->set_memory(map()->memory()); - sfpt->set_control(map()->control()); + SafePointNode* sfpt = create_safepoint_with_state_before_array_allocation(alloc); JVMState* saved_jvms = jvms(); saved_reexecute_sp = _reexecute_sp; @@ -4427,6 +4426,30 @@ JVMState* LibraryCallKit::arraycopy_restore_alloc_state(AllocateArrayNode* alloc return NULL; } +// Clone the JVMState of the array allocation and create a new safepoint with it. Re-push the array length to the stack +// such that uncommon traps can be emitted to re-execute the array allocation in the interpreter. +SafePointNode* LibraryCallKit::create_safepoint_with_state_before_array_allocation(const AllocateArrayNode* alloc) const { + JVMState* old_jvms = alloc->jvms()->clone_shallow(C); + uint size = alloc->req(); + SafePointNode* sfpt = new SafePointNode(size, old_jvms); + old_jvms->set_map(sfpt); + for (uint i = 0; i < size; i++) { + sfpt->init_req(i, alloc->in(i)); + } + // re-push array length for deoptimization + sfpt->ins_req(old_jvms->stkoff() + old_jvms->sp(), alloc->in(AllocateNode::ALength)); + old_jvms->set_sp(old_jvms->sp()+1); + old_jvms->set_monoff(old_jvms->monoff()+1); + old_jvms->set_scloff(old_jvms->scloff()+1); + old_jvms->set_endoff(old_jvms->endoff()+1); + old_jvms->set_should_reexecute(true); + + sfpt->set_i_o(map()->i_o()); + sfpt->set_memory(map()->memory()); + sfpt->set_control(map()->control()); + return sfpt; +} + // In case of a deoptimization, we restart execution at the // allocation, allocating a new array. We would leave an uninitialized // array in the heap that GCs wouldn't expect. Move the allocation @@ -4434,18 +4457,20 @@ JVMState* LibraryCallKit::arraycopy_restore_alloc_state(AllocateArrayNode* alloc // deoptimize. This is possible because tightly_coupled_allocation() // guarantees there's no observer of the allocated array at this point // and the control flow is simple enough. -void LibraryCallKit::arraycopy_move_allocation_here(AllocateArrayNode* alloc, Node* dest, JVMState* saved_jvms, +void LibraryCallKit::arraycopy_move_allocation_here(AllocateArrayNode* alloc, Node* dest, JVMState* saved_jvms_before_guards, int saved_reexecute_sp, uint new_idx) { - if (saved_jvms != NULL && !stopped()) { + if (saved_jvms_before_guards != NULL && !stopped()) { + replace_unrelated_uncommon_traps_with_alloc_state(alloc, saved_jvms_before_guards); + assert(alloc != NULL, "only with a tightly coupled allocation"); // restore JVM state to the state at the arraycopy - saved_jvms->map()->set_control(map()->control()); - assert(saved_jvms->map()->memory() == map()->memory(), "memory state changed?"); - assert(saved_jvms->map()->i_o() == map()->i_o(), "IO state changed?"); + saved_jvms_before_guards->map()->set_control(map()->control()); + assert(saved_jvms_before_guards->map()->memory() == map()->memory(), "memory state changed?"); + assert(saved_jvms_before_guards->map()->i_o() == map()->i_o(), "IO state changed?"); // If we've improved the types of some nodes (null check) while // emitting the guards, propagate them to the current state - map()->replaced_nodes().apply(saved_jvms->map(), new_idx); - set_jvms(saved_jvms); + map()->replaced_nodes().apply(saved_jvms_before_guards->map(), new_idx); + set_jvms(saved_jvms_before_guards); _reexecute_sp = saved_reexecute_sp; // Remove the allocation from above the guards @@ -4521,6 +4546,58 @@ void LibraryCallKit::arraycopy_move_allocation_here(AllocateArrayNode* alloc, No } } +// Unrelated UCTs between the array allocation and the array copy, which are considered safe by tightly_coupled_allocation(), +// need to be replaced by an UCT with a state before the array allocation (including the array length). This is necessary +// because we could hit one of these UCTs (which are executed before the emitted array copy guards and the actual array +// allocation which is moved down in arraycopy_move_allocation_here()). When later resuming execution in the interpreter, +// we would have wrongly skipped the array allocation. To prevent this, we resume execution at the array allocation in +// the interpreter similar to what we are doing for the newly emitted guards for the array copy. +void LibraryCallKit::replace_unrelated_uncommon_traps_with_alloc_state(AllocateArrayNode* alloc, + JVMState* saved_jvms_before_guards) { + if (saved_jvms_before_guards->map()->control()->is_IfProj()) { + // There is at least one unrelated uncommon trap which needs to be replaced. + SafePointNode* sfpt = create_safepoint_with_state_before_array_allocation(alloc); + + JVMState* saved_jvms = jvms(); + const int saved_reexecute_sp = _reexecute_sp; + set_jvms(sfpt->jvms()); + _reexecute_sp = jvms()->sp(); + + replace_unrelated_uncommon_traps_with_alloc_state(saved_jvms_before_guards); + + // Restore state + set_jvms(saved_jvms); + _reexecute_sp = saved_reexecute_sp; + } +} + +// Replace the unrelated uncommon traps with new uncommon trap nodes by reusing the action and reason. The new uncommon +// traps will have the state of the array allocation. Let the old uncommon trap nodes die. +void LibraryCallKit::replace_unrelated_uncommon_traps_with_alloc_state(JVMState* saved_jvms_before_guards) { + Node* if_proj = saved_jvms_before_guards->map()->control(); // Start the search right before the newly emitted guards + while (if_proj->is_IfProj()) { + CallStaticJavaNode* uncommon_trap = get_uncommon_trap_from_success_proj(if_proj); + if (uncommon_trap != nullptr) { + create_new_uncommon_trap(uncommon_trap); + } + assert(if_proj->in(0)->is_If(), "must be If"); + if_proj = if_proj->in(0)->in(0); + } + assert(if_proj->is_Proj() && if_proj->in(0)->is_Initialize(), + "must have reached control projection of init node"); +} + +void LibraryCallKit::create_new_uncommon_trap(CallStaticJavaNode* uncommon_trap_call) { + const int trap_request = uncommon_trap_call->uncommon_trap_request(); + assert(trap_request != 0, "no valid UCT trap request"); + PreserveJVMState pjvms(this); + set_control(uncommon_trap_call->in(0)); + uncommon_trap(Deoptimization::trap_request_reason(trap_request), + Deoptimization::trap_request_action(trap_request)); + assert(stopped(), "Should be stopped"); + _gvn.hash_delete(uncommon_trap_call); + uncommon_trap_call->set_req(0, top()); // not used anymore, kill it +} //------------------------------inline_arraycopy----------------------- // public static native void java.lang.System.arraycopy(Object src, int srcPos, @@ -4541,12 +4618,12 @@ bool LibraryCallKit::inline_arraycopy() { AllocateArrayNode* alloc = tightly_coupled_allocation(dest); int saved_reexecute_sp = -1; - JVMState* saved_jvms = arraycopy_restore_alloc_state(alloc, saved_reexecute_sp); + JVMState* saved_jvms_before_guards = arraycopy_restore_alloc_state(alloc, saved_reexecute_sp); // See arraycopy_restore_alloc_state() comment // if alloc == NULL we don't have to worry about a tightly coupled allocation so we can emit all needed guards - // if saved_jvms != NULL (then alloc != NULL) then we can handle guards and a tightly coupled allocation - // if saved_jvms == NULL and alloc != NULL, we can't emit any guards - bool can_emit_guards = (alloc == NULL || saved_jvms != NULL); + // if saved_jvms_before_guards != NULL (then alloc != NULL) then we can handle guards and a tightly coupled allocation + // if saved_jvms_before_guards == NULL and alloc != NULL, we can't emit any guards + bool can_emit_guards = (alloc == NULL || saved_jvms_before_guards != NULL); // The following tests must be performed // (1) src and dest are arrays. @@ -4562,12 +4639,12 @@ bool LibraryCallKit::inline_arraycopy() { // (3) src and dest must not be null. // always do this here because we need the JVM state for uncommon traps Node* null_ctl = top(); - src = saved_jvms != NULL ? null_check_oop(src, &null_ctl, true, true) : null_check(src, T_ARRAY); + src = saved_jvms_before_guards != NULL ? null_check_oop(src, &null_ctl, true, true) : null_check(src, T_ARRAY); assert(null_ctl->is_top(), "no null control here"); dest = null_check(dest, T_ARRAY); if (!can_emit_guards) { - // if saved_jvms == NULL and alloc != NULL, we don't emit any + // if saved_jvms_before_guards == NULL and alloc != NULL, we don't emit any // guards but the arraycopy node could still take advantage of a // tightly allocated allocation. tightly_coupled_allocation() is // called again to make sure it takes the null check above into @@ -4681,7 +4758,7 @@ bool LibraryCallKit::inline_arraycopy() { ciMethod* trap_method = method(); int trap_bci = bci(); - if (saved_jvms != NULL) { + if (saved_jvms_before_guards != NULL) { trap_method = alloc->jvms()->method(); trap_bci = alloc->jvms()->bci(); } @@ -4753,10 +4830,9 @@ bool LibraryCallKit::inline_arraycopy() { const TypeKlassPtr* dest_klass_t = _gvn.type(dest_klass)->is_klassptr(); const Type *toop = TypeOopPtr::make_from_klass(dest_klass_t->klass()); src = _gvn.transform(new CheckCastPPNode(control(), src, toop)); + arraycopy_move_allocation_here(alloc, dest, saved_jvms_before_guards, saved_reexecute_sp, new_idx); } - arraycopy_move_allocation_here(alloc, dest, saved_jvms, saved_reexecute_sp, new_idx); - if (stopped()) { return true; } @@ -4821,28 +4897,15 @@ LibraryCallKit::tightly_coupled_allocation(Node* ptr) { // There may be guards which feed into the slow_region. // Any other control flow means that we might not get a chance // to finish initializing the allocated object. - if ((ctl->is_IfFalse() || ctl->is_IfTrue()) && ctl->in(0)->is_If()) { - IfNode* iff = ctl->in(0)->as_If(); - Node* not_ctl = iff->proj_out_or_null(1 - ctl->as_Proj()->_con); - assert(not_ctl != NULL && not_ctl != ctl, "found alternate"); - // One more try: Various low-level checks bottom out in - // uncommon traps. If the debug-info of the trap omits - // any reference to the allocation, as we've already - // observed, then there can be no objection to the trap. - bool found_trap = false; - for (DUIterator_Fast jmax, j = not_ctl->fast_outs(jmax); j < jmax; j++) { - Node* obs = not_ctl->fast_out(j); - if (obs->in(0) == not_ctl && obs->is_Call() && - (obs->as_Call()->entry_point() == SharedRuntime::uncommon_trap_blob()->entry_point())) { - found_trap = true; break; - } - } - if (found_trap) { - ctl = iff->in(0); // This test feeds a harmless uncommon trap. - continue; - } + // Various low-level checks bottom out in uncommon traps. These + // are considered safe since we've already checked above that + // there is no unexpected observer of this allocation. + if (get_uncommon_trap_from_success_proj(ctl) != nullptr) { + assert(ctl->in(0)->is_If(), "must be If"); + ctl = ctl->in(0)->in(0); + } else { + return nullptr; } - return NULL; } // If we get this far, we have an allocation which immediately @@ -4853,6 +4916,20 @@ LibraryCallKit::tightly_coupled_allocation(Node* ptr) { return alloc; } +CallStaticJavaNode* LibraryCallKit::get_uncommon_trap_from_success_proj(Node* node) { + if (node->is_IfProj()) { + Node* other_proj = node->as_IfProj()->other_if_proj(); + for (DUIterator_Fast jmax, j = other_proj->fast_outs(jmax); j < jmax; j++) { + Node* obs = other_proj->fast_out(j); + if (obs->in(0) == other_proj && obs->is_CallStaticJava() && + (obs->as_CallStaticJava()->entry_point() == SharedRuntime::uncommon_trap_blob()->entry_point())) { + return obs->as_CallStaticJava(); + } + } + } + return nullptr; +} + //-------------inline_encodeISOArray----------------------------------- // encode char[] to byte[] in ISO_8859_1 or ASCII bool LibraryCallKit::inline_encodeISOArray(bool ascii) { @@ -4878,8 +4955,8 @@ bool LibraryCallKit::inline_encodeISOArray(bool ascii) { } // Figure out the size and type of the elements we will be copying. - BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType dst_elem = dst_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); + BasicType dst_elem = top_dest->klass()->as_array_klass()->element_type()->basic_type(); if (!((src_elem == T_CHAR) || (src_elem== T_BYTE)) || dst_elem != T_BYTE) { return false; } @@ -4932,8 +5009,8 @@ bool LibraryCallKit::inline_multiplyToLen() { return false; } - BasicType x_elem = x_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType y_elem = y_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType x_elem = top_x->klass()->as_array_klass()->element_type()->basic_type(); + BasicType y_elem = top_y->klass()->as_array_klass()->element_type()->basic_type(); if (x_elem != T_INT || y_elem != T_INT) { return false; } @@ -5040,8 +5117,8 @@ bool LibraryCallKit::inline_squareToLen() { return false; } - BasicType x_elem = x_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType z_elem = z_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType x_elem = top_x->klass()->as_array_klass()->element_type()->basic_type(); + BasicType z_elem = top_z->klass()->as_array_klass()->element_type()->basic_type(); if (x_elem != T_INT || z_elem != T_INT) { return false; } @@ -5089,8 +5166,8 @@ bool LibraryCallKit::inline_mulAdd() { return false; } - BasicType out_elem = out_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType in_elem = in_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType out_elem = top_out->klass()->as_array_klass()->element_type()->basic_type(); + BasicType in_elem = top_in->klass()->as_array_klass()->element_type()->basic_type(); if (out_elem != T_INT || in_elem != T_INT) { return false; } @@ -5144,10 +5221,10 @@ bool LibraryCallKit::inline_montgomeryMultiply() { return false; } - BasicType a_elem = a_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType b_elem = b_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType n_elem = n_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType m_elem = m_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType a_elem = top_a->klass()->as_array_klass()->element_type()->basic_type(); + BasicType b_elem = top_b->klass()->as_array_klass()->element_type()->basic_type(); + BasicType n_elem = top_n->klass()->as_array_klass()->element_type()->basic_type(); + BasicType m_elem = top_m->klass()->as_array_klass()->element_type()->basic_type(); if (a_elem != T_INT || b_elem != T_INT || n_elem != T_INT || m_elem != T_INT) { return false; } @@ -5200,9 +5277,9 @@ bool LibraryCallKit::inline_montgomerySquare() { return false; } - BasicType a_elem = a_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType n_elem = n_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType m_elem = m_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType a_elem = top_a->klass()->as_array_klass()->element_type()->basic_type(); + BasicType n_elem = top_n->klass()->as_array_klass()->element_type()->basic_type(); + BasicType m_elem = top_m->klass()->as_array_klass()->element_type()->basic_type(); if (a_elem != T_INT || n_elem != T_INT || m_elem != T_INT) { return false; } @@ -5252,8 +5329,8 @@ bool LibraryCallKit::inline_bigIntegerShift(bool isRightShift) { return false; } - BasicType newArr_elem = newArr_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); - BasicType oldArr_elem = oldArr_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType newArr_elem = top_newArr->klass()->as_array_klass()->element_type()->basic_type(); + BasicType oldArr_elem = top_oldArr->klass()->as_array_klass()->element_type()->basic_type(); if (newArr_elem != T_INT || oldArr_elem != T_INT) { return false; } @@ -5466,7 +5543,7 @@ bool LibraryCallKit::inline_updateBytesCRC32() { } // Figure out the size and type of the elements we will be copying. - BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); if (src_elem != T_BYTE) { return false; } @@ -5555,7 +5632,7 @@ bool LibraryCallKit::inline_updateBytesCRC32C() { } // Figure out the size and type of the elements we will be copying. - BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); if (src_elem != T_BYTE) { return false; } @@ -5648,7 +5725,7 @@ bool LibraryCallKit::inline_updateBytesAdler32() { } // Figure out the size and type of the elements we will be copying. - BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); if (src_elem != T_BYTE) { return false; } @@ -6485,7 +6562,7 @@ bool LibraryCallKit::inline_digestBase_implCompress(vmIntrinsics::ID id) { return false; } // Figure out the size and type of the elements we will be copying. - BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); if (src_elem != T_BYTE) { return false; } @@ -6577,7 +6654,7 @@ bool LibraryCallKit::inline_digestBase_implCompressMB(int predicate) { return false; } // Figure out the size and type of the elements we will be copying. - BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); if (src_elem != T_BYTE) { return false; } @@ -7109,8 +7186,15 @@ bool LibraryCallKit::inline_blackhole() { assert(callee()->is_empty(), "Should have been checked before: only empty methods here"); assert(callee()->holder()->is_loaded(), "Should have been checked before: only methods for loaded classes here"); + // Blackhole node pinches only the control, not memory. This allows + // the blackhole to be pinned in the loop that computes blackholed + // values, but have no other side effects, like breaking the optimizations + // across the blackhole. + + Node* bh = _gvn.transform(new BlackholeNode(control())); + set_control(_gvn.transform(new ProjNode(bh, TypeFunc::Control))); + // Bind call arguments as blackhole arguments to keep them alive - Node* bh = insert_mem_bar(Op_Blackhole); uint nargs = callee()->arg_size(); for (uint i = 0; i < nargs; i++) { bh->add_req(argument(i)); diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 15e2de48bec..9b46475fb2f 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -253,8 +253,13 @@ class LibraryCallKit : public GraphKit { // Helper functions for inlining arraycopy bool inline_arraycopy(); AllocateArrayNode* tightly_coupled_allocation(Node* ptr); + static CallStaticJavaNode* get_uncommon_trap_from_success_proj(Node* node); + SafePointNode* create_safepoint_with_state_before_array_allocation(const AllocateArrayNode* alloc) const; + void replace_unrelated_uncommon_traps_with_alloc_state(AllocateArrayNode* alloc, JVMState* saved_jvms_before_guards); + void replace_unrelated_uncommon_traps_with_alloc_state(JVMState* saved_jvms_before_guards); + void create_new_uncommon_trap(CallStaticJavaNode* uncommon_trap_call); JVMState* arraycopy_restore_alloc_state(AllocateArrayNode* alloc, int& saved_reexecute_sp); - void arraycopy_move_allocation_here(AllocateArrayNode* alloc, Node* dest, JVMState* saved_jvms, int saved_reexecute_sp, + void arraycopy_move_allocation_here(AllocateArrayNode* alloc, Node* dest, JVMState* saved_jvms_before_guards, int saved_reexecute_sp, uint new_idx); typedef enum { LS_get_add, LS_get_set, LS_cmp_swap, LS_cmp_swap_weak, LS_cmp_exchange } LoadStoreKind; diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index 9ae91840116..29c2c03abf7 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -1343,11 +1343,13 @@ bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree *loop, ProjNode* upper_bound_iff->set_req(1, upper_bound_bol); if (TraceLoopPredicate) tty->print_cr("upper bound check if: %s %d ", negate ? " negated" : "", lower_bound_iff->_idx); - // Fall through into rest of the cleanup code which will move any dependent nodes to the skeleton predicates of the - // upper bound test. We always need to create skeleton predicates in order to properly remove dead loops when later - // splitting the predicated loop into (unreachable) sub-loops (i.e. done by unrolling, peeling, pre/main/post etc.). - new_predicate_proj = insert_initial_skeleton_predicate(iff, loop, proj, predicate_proj, upper_bound_proj, scale, - offset, init, limit, stride, rng, overflow, reason); + // Fall through into rest of the clean up code which will move + // any dependent nodes onto the upper bound test. + new_predicate_proj = upper_bound_proj; + + if (iff->is_RangeCheck()) { + new_predicate_proj = insert_initial_skeleton_predicate(iff, loop, proj, predicate_proj, upper_bound_proj, scale, offset, init, limit, stride, rng, overflow, reason); + } #ifndef PRODUCT if (TraceLoopOpts && !TraceLoopPredicate) { diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index f7e7073dd90..c52f84cd016 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -2149,8 +2149,8 @@ void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adj // Verify that policy_unroll result is still valid. const TypeInt* limit_type = _igvn.type(limit)->is_int(); - assert(stride_con > 0 && ((limit_type->_hi - stride_con) < limit_type->_hi) || - stride_con < 0 && ((limit_type->_lo - stride_con) > limit_type->_lo), + assert((stride_con > 0 && ((min_jint + stride_con) <= limit_type->_hi)) || + (stride_con < 0 && ((max_jint + stride_con) >= limit_type->_lo)), "sanity"); if (limit->is_Con()) { @@ -3947,6 +3947,19 @@ bool PhaseIdealLoop::intrinsify_fill(IdealLoopTree* lpt) { Node* len = new SubINode(head->limit(), head->init_trip()); _igvn.register_new_node_with_optimizer(len); + // If the store is on the backedge, it is not executed in the last + // iteration, and we must subtract 1 from the len. + Node* backedge = head->loopexit()->proj_out(1); + if (store->in(0) == backedge) { + len = new SubINode(len, _igvn.intcon(1)); + _igvn.register_new_node_with_optimizer(len); +#ifndef PRODUCT + if (TraceOptimizeFill) { + tty->print_cr("ArrayFill store on backedge, subtract 1 from len."); + } +#endif + } + BasicType t = store->as_Mem()->memory_type(); bool aligned = false; if (offset != NULL && head->init_trip()->is_Con()) { @@ -4049,5 +4062,12 @@ bool PhaseIdealLoop::intrinsify_fill(IdealLoopTree* lpt) { _igvn.replace_node(n, C->top()); } +#ifndef PRODUCT + if (TraceOptimizeFill) { + tty->print("ArrayFill call "); + call->dump(); + } +#endif + return true; } diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index f26df6f239b..a71c1ee9b1f 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -3123,6 +3123,17 @@ void IdealLoopTree::check_safepts(VectorSet &visited, Node_List &stack) { // Skip to head of inner loop assert(_phase->is_dominator(_head, nlpt->_head), "inner head dominated by outer head"); n = nlpt->_head; + if (_head == n) { + // this and nlpt (inner loop) have the same loop head. This should not happen because + // during beautify_loops we call merge_many_backedges. However, infinite loops may not + // have been attached to the loop-tree during build_loop_tree before beautify_loops, + // but then attached in the build_loop_tree afterwards, and so still have unmerged + // backedges. Check if we are indeed in an infinite subgraph, and terminate the scan, + // since we have reached the loop head of this. + assert(_head->as_Region()->is_in_infinite_subgraph(), + "only expect unmerged backedges in infinite loops"); + break; + } } } } @@ -3704,30 +3715,7 @@ bool PhaseIdealLoop::only_has_infinite_loops() { assert(head->is_Region(), ""); worklist.push(head); } - // BFS traversal down the CFG, except through NeverBranch exits - for (uint i = 0; i < worklist.size(); ++i) { - Node* n = worklist.at(i); - assert(n->is_CFG(), "only traverse CFG"); - if (n->is_Root()) { - // Found root -> there was an exit! - return false; - } else if (n->is_NeverBranch()) { - // Only follow the loop-internal projection, not the NeverBranch exit - ProjNode* proj = n->as_NeverBranch()->proj_out_or_null(0); - assert(proj != nullptr, "must find loop-internal projection of NeverBranch"); - worklist.push(proj); - } else { - // Traverse all CFG outputs - for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { - Node* use = n->fast_out(i); - if (use->is_CFG()) { - worklist.push(use); - } - } - } - } - // No exit found for any loop -> all are infinite - return true; + return RegionNode::are_all_nodes_in_infinite_subgraph(worklist); } #endif diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index ebc3bd1dbf8..0db6d088145 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1506,6 +1506,9 @@ class PhaseIdealLoop : public PhaseTransform { void try_move_store_after_loop(Node* n); bool identical_backtoback_ifs(Node *n); bool can_split_if(Node *n_ctrl); + bool cannot_split_division(const Node* n, const Node* region) const; + static bool is_divisor_counted_loop_phi(const Node* divisor, const Node* loop); + bool loop_phi_backedge_type_contains_zero(const Node* phi_divisor, const Type* zero) const; // Determine if a method is too big for a/another round of split-if, based on // a magic (approximate) ratio derived from the equally magic constant 35000, diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index c0804ed677c..c179e954c80 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,19 +61,8 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { return NULL; } - // Bail out if 'n' is a Div or Mod node whose zero check was removed earlier (i.e. control is NULL) and its divisor is an induction variable - // phi p of a trip-counted (integer) loop whose inputs could be zero (include zero in their type range). p could have a more precise type - // range that does not necessarily include all values of its inputs. Since each of these inputs will be a divisor of the newly cloned nodes - // of 'n', we need to bail out of one of these divisors could be zero (zero in its type range). - if ((n->Opcode() == Op_DivI || n->Opcode() == Op_ModI) && n->in(0) == NULL - && region->is_CountedLoop() && n->in(2) == region->as_CountedLoop()->phi()) { - Node* phi = region->as_CountedLoop()->phi(); - for (uint i = 1; i < phi->req(); i++) { - if (_igvn.type(phi->in(i))->filter_speculative(TypeInt::ZERO) != Type::TOP) { - // Zero could be a possible value but we already removed the zero check. Bail out to avoid a possible division by zero at a later point. - return NULL; - } - } + if (cannot_split_division(n, region)) { + return NULL; } int wins = 0; @@ -225,6 +214,42 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) { return phi; } +// Return true if 'n' is a Div or Mod node (without zero check If node which was removed earlier) with a loop phi divisor +// of a trip-counted (integer or long) loop with a backedge input that could be zero (include zero in its type range). In +// this case, we cannot split the division to the backedge as it could freely float above the loop exit check resulting in +// a division by zero. This situation is possible because the type of an increment node of an iv phi (trip-counter) could +// include zero while the iv phi does not (see PhiNode::Value() for trip-counted loops where we improve types of iv phis). +// We also need to check other loop phis as they could have been created in the same split-if pass when applying +// PhaseIdealLoop::split_thru_phi() to split nodes through an iv phi. +bool PhaseIdealLoop::cannot_split_division(const Node* n, const Node* region) const { + const Type* zero; + switch (n->Opcode()) { + case Op_DivI: + case Op_ModI: + zero = TypeInt::ZERO; + break; + case Op_DivL: + case Op_ModL: + zero = TypeLong::ZERO; + break; + default: + return false; + } + + assert(n->in(0) == NULL, "divisions with zero check should already have bailed out earlier in split-if"); + Node* divisor = n->in(2); + return is_divisor_counted_loop_phi(divisor, region) && + loop_phi_backedge_type_contains_zero(divisor, zero); +} + +bool PhaseIdealLoop::is_divisor_counted_loop_phi(const Node* divisor, const Node* loop) { + return loop->is_BaseCountedLoop() && divisor->is_Phi() && divisor->in(0) == loop; +} + +bool PhaseIdealLoop::loop_phi_backedge_type_contains_zero(const Node* phi_divisor, const Type* zero) const { + return _igvn.type(phi_divisor->in(LoopNode::LoopBackControl))->filter_speculative(zero) != Type::TOP; +} + //------------------------------dominated_by------------------------------------ // Replace the dominated test with an obvious true or false. Place it on the // IGVN worklist for later cleanup. Move control-dependent data Nodes on the @@ -484,7 +509,7 @@ Node *PhaseIdealLoop::remix_address_expressions( Node *n ) { n23_loop == n_loop ) { Node *add1 = new AddPNode( n->in(1), n->in(2)->in(2), n->in(3) ); // Stuff new AddP in the loop preheader - register_new_node( add1, n_loop->_head->in(LoopNode::EntryControl) ); + register_new_node( add1, n_loop->_head->as_Loop()->skip_strip_mined(1)->in(LoopNode::EntryControl) ); Node *add2 = new AddPNode( n->in(1), add1, n->in(2)->in(3) ); register_new_node( add2, n_ctrl ); _igvn.replace_node( n, add2 ); @@ -505,7 +530,7 @@ Node *PhaseIdealLoop::remix_address_expressions( Node *n ) { if (!is_member(n_loop,get_ctrl(I))) { Node *add1 = new AddPNode(n->in(1), n->in(2), I); // Stuff new AddP in the loop preheader - register_new_node(add1, n_loop->_head->in(LoopNode::EntryControl)); + register_new_node(add1, n_loop->_head->as_Loop()->skip_strip_mined(1)->in(LoopNode::EntryControl)); Node *add2 = new AddPNode(n->in(1), add1, V); register_new_node(add2, n_ctrl); _igvn.replace_node(n, add2); @@ -1309,8 +1334,8 @@ void PhaseIdealLoop::split_if_with_blocks_post(Node *n) { return; // Compare must be in same blk as if } } else if (iff->is_CMove()) { // Trying to split-up a CMOVE - // Can't split CMove with different control edge. - if (iff->in(0) != NULL && iff->in(0) != n_ctrl ) { + // Can't split CMove with different control. + if (get_ctrl(iff) != n_ctrl) { return; } if (get_ctrl(iff->in(2)) == n_ctrl || @@ -1928,7 +1953,13 @@ void PhaseIdealLoop::clone_loop_handle_data_uses(Node* old, Node_List &old_new, // in the loop to break the loop, then test is again outside of the // loop to determine which way the loop exited. // Loop predicate If node connects to Bool node through Opaque1 node. - if (use->is_If() || use->is_CMove() || C->is_predicate_opaq(use) || use->Opcode() == Op_Opaque4) { + // + // If the use is an AllocateArray through its ValidLengthTest input, + // make sure the Bool/Cmp input is cloned down to avoid a Phi between + // the AllocateArray node and its ValidLengthTest input that could cause + // split if to break. + if (use->is_If() || use->is_CMove() || C->is_predicate_opaq(use) || use->Opcode() == Op_Opaque4 || + (use->Opcode() == Op_AllocateArray && use->in(AllocateNode::ValidLengthTest) == old)) { // Since this code is highly unlikely, we lazily build the worklist // of such Nodes to go split. if (!split_if_set) { @@ -2442,9 +2473,10 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd if (split_if_set) { while (split_if_set->size()) { Node *iff = split_if_set->pop(); - if (iff->in(1)->is_Phi()) { - Node *b = clone_iff(iff->in(1)->as_Phi(), loop); - _igvn.replace_input_of(iff, 1, b); + uint input = iff->Opcode() == Op_AllocateArray ? AllocateNode::ValidLengthTest : 1; + if (iff->in(input)->is_Phi()) { + Node *b = clone_iff(iff->in(input)->as_Phi(), loop); + _igvn.replace_input_of(iff, input, b); } } } diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 673784d2aab..829cc4c8aed 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -1205,7 +1205,8 @@ void PhaseMacroExpand::expand_allocate_common( AllocateNode* alloc, // allocation node to be expanded Node* length, // array length for an array allocation const TypeFunc* slow_call_type, // Type of slow call - address slow_call_address // Address of slow call + address slow_call_address, // Address of slow call + Node* valid_length_test // whether length is valid or not ) { Node* ctrl = alloc->in(TypeFunc::Control); @@ -1391,6 +1392,12 @@ void PhaseMacroExpand::expand_allocate_common( // Copy debug information and adjust JVMState information, then replace // allocate node with the call call->copy_call_debug_info(&_igvn, alloc); + // For array allocations, copy the valid length check to the call node so Compile::final_graph_reshaping() can verify + // that the call has the expected number of CatchProj nodes (in case the allocation always fails and the fallthrough + // path dies). + if (valid_length_test != NULL) { + call->add_req(valid_length_test); + } if (expand_fast_path) { call->set_cnt(PROB_UNLIKELY_MAG(4)); // Same effect as RC_UNCOMMON. } else { @@ -1872,11 +1879,12 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, void PhaseMacroExpand::expand_allocate(AllocateNode *alloc) { expand_allocate_common(alloc, NULL, OptoRuntime::new_instance_Type(), - OptoRuntime::new_instance_Java()); + OptoRuntime::new_instance_Java(), NULL); } void PhaseMacroExpand::expand_allocate_array(AllocateArrayNode *alloc) { Node* length = alloc->in(AllocateNode::ALength); + Node* valid_length_test = alloc->in(AllocateNode::ValidLengthTest); InitializeNode* init = alloc->initialization(); Node* klass_node = alloc->in(AllocateNode::KlassNode); ciKlass* k = _igvn.type(klass_node)->is_klassptr()->klass(); @@ -1891,7 +1899,7 @@ void PhaseMacroExpand::expand_allocate_array(AllocateArrayNode *alloc) { } expand_allocate_common(alloc, length, OptoRuntime::new_array_Type(), - slow_call_address); + slow_call_address, valid_length_test); } //-------------------mark_eliminated_box---------------------------------- diff --git a/src/hotspot/share/opto/macro.hpp b/src/hotspot/share/opto/macro.hpp index 413e8d04c4b..6aa44220f31 100644 --- a/src/hotspot/share/opto/macro.hpp +++ b/src/hotspot/share/opto/macro.hpp @@ -92,8 +92,8 @@ class PhaseMacroExpand : public Phase { void expand_allocate_common(AllocateNode* alloc, Node* length, const TypeFunc* slow_call_type, - address slow_call_address); - void yank_initalize_node(InitializeNode* node); + address slow_call_address, + Node* valid_length_test); void yank_alloc_node(AllocateNode* alloc); Node *value_from_mem(Node *mem, Node *ctl, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc); Node *value_from_mem_phi(Node *mem, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc, Node_Stack *value_phis, int level); diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 84efd9ff75d..37848a2793b 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -1054,7 +1054,8 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const { opc == Op_MemBarRelease || opc == Op_StoreFence || opc == Op_MemBarReleaseLock || - opc == Op_MemBarStoreStore) { + opc == Op_MemBarStoreStore || + opc == Op_StoreStoreFence) { Node* mem = current->in(0)->in(TypeFunc::Memory); if (mem->is_MergeMem()) { MergeMemNode* merge = mem->as_MergeMem(); @@ -3286,14 +3287,14 @@ MemBarNode* MemBarNode::make(Compile* C, int opcode, int atp, Node* pn) { case Op_LoadFence: return new LoadFenceNode(C, atp, pn); case Op_MemBarRelease: return new MemBarReleaseNode(C, atp, pn); case Op_StoreFence: return new StoreFenceNode(C, atp, pn); + case Op_MemBarStoreStore: return new MemBarStoreStoreNode(C, atp, pn); + case Op_StoreStoreFence: return new StoreStoreFenceNode(C, atp, pn); case Op_MemBarAcquireLock: return new MemBarAcquireLockNode(C, atp, pn); case Op_MemBarReleaseLock: return new MemBarReleaseLockNode(C, atp, pn); case Op_MemBarVolatile: return new MemBarVolatileNode(C, atp, pn); case Op_MemBarCPUOrder: return new MemBarCPUOrderNode(C, atp, pn); case Op_OnSpinWait: return new OnSpinWaitNode(C, atp, pn); case Op_Initialize: return new InitializeNode(C, atp, pn); - case Op_MemBarStoreStore: return new MemBarStoreStoreNode(C, atp, pn); - case Op_Blackhole: return new BlackholeNode(C, atp, pn); default: ShouldNotReachHere(); return NULL; } } @@ -3533,26 +3534,6 @@ MemBarNode* MemBarNode::leading_membar() const { return mb; } -#ifndef PRODUCT -void BlackholeNode::format(PhaseRegAlloc* ra, outputStream* st) const { - st->print("blackhole "); - bool first = true; - for (uint i = 0; i < req(); i++) { - Node* n = in(i); - if (n != NULL && OptoReg::is_valid(ra->get_reg_first(n))) { - if (first) { - first = false; - } else { - st->print(", "); - } - char buf[128]; - ra->dump_register(n, buf); - st->print("%s", buf); - } - } - st->cr(); -} -#endif //===========================InitializeNode==================================== // SUMMARY: diff --git a/src/hotspot/share/opto/memnode.hpp b/src/hotspot/share/opto/memnode.hpp index 04d24c4cd9a..f24b6de6196 100644 --- a/src/hotspot/share/opto/memnode.hpp +++ b/src/hotspot/share/opto/memnode.hpp @@ -1309,6 +1309,13 @@ class MemBarStoreStoreNode: public MemBarNode { virtual int Opcode() const; }; +class StoreStoreFenceNode: public MemBarNode { +public: + StoreStoreFenceNode(Compile* C, int alias_idx, Node* precedent) + : MemBarNode(C, alias_idx, precedent) {} + virtual int Opcode() const; +}; + // Ordering between a volatile store and a following volatile load. // Requires multi-CPU visibility? class MemBarVolatileNode: public MemBarNode { @@ -1336,26 +1343,6 @@ class OnSpinWaitNode: public MemBarNode { virtual int Opcode() const; }; -//------------------------------BlackholeNode---------------------------- -// Blackhole all arguments. This node would survive through the compiler -// the effects on its arguments, and would be finally matched to nothing. -class BlackholeNode : public MemBarNode { -public: - BlackholeNode(Compile* C, int alias_idx, Node* precedent) - : MemBarNode(C, alias_idx, precedent) {} - virtual int Opcode() const; - virtual uint ideal_reg() const { return 0; } // not matched in the AD file - const RegMask &in_RegMask(uint idx) const { - // Fake the incoming arguments mask for blackholes: accept all registers - // and all stack slots. This would avoid any redundant register moves - // for blackhole inputs. - return RegMask::All; - } -#ifndef PRODUCT - virtual void format(PhaseRegAlloc* ra, outputStream* st) const; -#endif -}; - // Isolation of object setup after an AllocateNode and before next safepoint. // (See comment in memnode.cpp near InitializeNode::InitializeNode for semantics.) class InitializeNode: public MemBarNode { diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 2dad491977a..9a9d3f0149a 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -622,33 +622,7 @@ void Node::destruct(PhaseValues* phase) { //assert(def->out(def->outcnt()-1) == (Node *)this,"bad def-use hacking in reclaim"); } assert(outcnt() == 0, "deleting a node must not leave a dangling use"); - // See if the input array was allocated just prior to the object - int edge_size = _max*sizeof(void*); - int out_edge_size = _outmax*sizeof(void*); - char *edge_end = ((char*)_in) + edge_size; - char *out_array = (char*)(_out == NO_OUT_ARRAY? NULL: _out); - int node_size = size_of(); - // Free the output edge array - if (out_edge_size > 0) { - compile->node_arena()->Afree(out_array, out_edge_size); - } - - // Free the input edge array and the node itself - if( edge_end == (char*)this ) { - // It was; free the input array and object all in one hit -#ifndef ASSERT - compile->node_arena()->Afree(_in,edge_size+node_size); -#endif - } else { - // Free just the input array - compile->node_arena()->Afree(_in,edge_size); - - // Free just the object -#ifndef ASSERT - compile->node_arena()->Afree(this,node_size); -#endif - } if (is_macro()) { compile->remove_macro_node(this); } @@ -667,13 +641,43 @@ void Node::destruct(PhaseValues* phase) { } BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); bs->unregister_potential_barrier_node(this); + + // See if the input array was allocated just prior to the object + int edge_size = _max*sizeof(void*); + int out_edge_size = _outmax*sizeof(void*); + char *in_array = ((char*)_in); + char *edge_end = in_array + edge_size; + char *out_array = (char*)(_out == NO_OUT_ARRAY? NULL: _out); + int node_size = size_of(); + #ifdef ASSERT // We will not actually delete the storage, but we'll make the node unusable. + compile->remove_modified_node(this); *(address*)this = badAddress; // smash the C++ vtbl, probably _in = _out = (Node**) badAddress; _max = _cnt = _outmax = _outcnt = 0; - compile->remove_modified_node(this); #endif + + // Free the output edge array + if (out_edge_size > 0) { + compile->node_arena()->Afree(out_array, out_edge_size); + } + + // Free the input edge array and the node itself + if( edge_end == (char*)this ) { + // It was; free the input array and object all in one hit +#ifndef ASSERT + compile->node_arena()->Afree(in_array, edge_size+node_size); +#endif + } else { + // Free just the input array + compile->node_arena()->Afree(in_array, edge_size); + + // Free just the object +#ifndef ASSERT + compile->node_arena()->Afree(this, node_size); +#endif + } } //------------------------------grow------------------------------------------- diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 2a78e259d3e..b79e7673f13 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -1647,6 +1647,11 @@ inline void Compile::record_for_igvn(Node* n) { _for_igvn->push(n); } +// Inline definition of Compile::remove_for_igvn must be deferred to this point. +inline void Compile::remove_for_igvn(Node* n) { + _for_igvn->remove(n); +} + //------------------------------Node_Stack------------------------------------- class Node_Stack { friend class VMStructs; diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index 7819812e1fe..c895f12354f 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -613,8 +613,6 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) // Parse all the basic blocks. do_all_blocks(); - C->set_default_node_notes(caller_nn); - // Check for bailouts during conversion to graph if (failing()) { if (log) log->done("parse"); @@ -625,6 +623,10 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) set_map(entry_map); do_exits(); + // Only reset this now, to make sure that debug information emitted + // for exiting control flow still refers to the inlined method. + C->set_default_node_notes(caller_nn); + if (log) log->done("parse nodes='%d' live='%d' memory='" SIZE_FORMAT "'", C->unique(), C->live_nodes(), C->node_arena()->used()); } diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 1600e1f82d7..f39f07e7e7f 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -469,6 +469,14 @@ PhaseRenumberLive::PhaseRenumberLive(PhaseGVN* gvn, uint worklist_size = worklist->size(); + GrowableArray* old_node_note_array = C->node_note_array(); + if (old_node_note_array != nullptr) { + int new_size = (_useful.size() >> 8) + 1; // The node note array uses blocks, see C->_log2_node_notes_block_size + new_size = MAX2(8, new_size); + C->set_node_note_array(new (C->comp_arena()) GrowableArray (C->comp_arena(), new_size, 0, nullptr)); + C->grow_node_notes(C->node_note_array(), new_size); + } + // Iterate over the set of live nodes. for (uint current_idx = 0; current_idx < _useful.size(); current_idx++) { Node* n = _useful.at(current_idx); @@ -484,6 +492,11 @@ PhaseRenumberLive::PhaseRenumberLive(PhaseGVN* gvn, assert(_old2new_map.at(n->_idx) == -1, "already seen"); _old2new_map.at_put(n->_idx, current_idx); + if (old_node_note_array != nullptr) { + Node_Notes* nn = C->locate_node_notes(old_node_note_array, n->_idx); + C->set_node_notes_at(current_idx, nn); + } + n->set_idx(current_idx); // Update node ID. if (in_worklist) { @@ -1642,6 +1655,16 @@ void PhaseIterGVN::add_users_to_worklist( Node *n ) { if (imem != NULL) add_users_to_worklist0(imem); } } + // If the ValidLengthTest input changes then the fallthrough path out of the AllocateArray may have become dead. + // CatchNode::Value() is responsible for killing that path. The CatchNode has to be explicitly enqueued for igvn + // to guarantee the change is not missed. + if (use_op == Op_AllocateArray && n == use->in(AllocateNode::ValidLengthTest)) { + Node* p = use->as_AllocateArray()->proj_out_or_null(TypeFunc::Control); + if (p != NULL) { + add_users_to_worklist0(p); + } + } + if (use_op == Op_Initialize) { Node* imem = use->as_Initialize()->proj_out_or_null(TypeFunc::Memory); if (imem != NULL) add_users_to_worklist0(imem); @@ -1808,6 +1831,7 @@ void PhaseCCP::analyze() { // the Catch following the call. It's looking for a non-NULL // receiver to know when to enable the regular fall-through path // in addition to the NullPtrException path + // Same is true if the type of a ValidLengthTest input to an AllocateArrayNode changes. if (m->is_Call()) { for (DUIterator_Fast i2max, i2 = m->fast_outs(i2max); i2 < i2max; i2++) { Node* p = m->fast_out(i2); // Propagate changes to uses diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index 6d0d8ca4665..25276116153 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -238,6 +238,11 @@ class PhaseTransform : public Phase { assert(t != NULL, "type must not be null"); _types.map(n->_idx, t); } + void clear_type(const Node* n) { + if (n->_idx < _types.Size()) { + _types.map(n->_idx, NULL); + } + } // Record an initial type for a node, the node's bottom type. void set_type_bottom(const Node* n) { // Use this for initialization when bottom_type() (or better) is not handy. diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp index 012c1a69905..2c474618e46 100644 --- a/src/hotspot/share/opto/split_if.cpp +++ b/src/hotspot/share/opto/split_if.cpp @@ -128,8 +128,8 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) { } } else { // We might see an Opaque1 from a loop limit check here - assert(use->is_If() || use->is_CMove() || use->Opcode() == Op_Opaque1, "unexpected node type"); - Node *use_c = use->is_If() ? use->in(0) : get_ctrl(use); + assert(use->is_If() || use->is_CMove() || use->Opcode() == Op_Opaque1 || use->is_AllocateArray(), "unexpected node type"); + Node *use_c = (use->is_If() || use->is_AllocateArray()) ? use->in(0) : get_ctrl(use); if (use_c == blk1 || use_c == blk2) { assert(use->is_CMove(), "unexpected node type"); continue; @@ -166,14 +166,15 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) { --j; } else { // We might see an Opaque1 from a loop limit check here - assert(u->is_If() || u->is_CMove() || u->Opcode() == Op_Opaque1, "unexpected node type"); - assert(u->in(1) == bol, ""); + assert(u->is_If() || u->is_CMove() || u->Opcode() == Op_Opaque1 || u->is_AllocateArray(), "unexpected node type"); + assert(u->is_AllocateArray() || u->in(1) == bol, ""); + assert(!u->is_AllocateArray() || u->in(AllocateNode::ValidLengthTest) == bol, "wrong input to AllocateArray"); // Get control block of either the CMove or the If input - Node *u_ctrl = u->is_If() ? u->in(0) : get_ctrl(u); + Node *u_ctrl = (u->is_If() || u->is_AllocateArray()) ? u->in(0) : get_ctrl(u); assert((u_ctrl != blk1 && u_ctrl != blk2) || u->is_CMove(), "won't converge"); Node *x = bol->clone(); register_new_node(x, u_ctrl); - _igvn.replace_input_of(u, 1, x); + _igvn.replace_input_of(u, u->is_AllocateArray() ? AllocateNode::ValidLengthTest : 1, x); --j; } } diff --git a/src/hotspot/share/opto/subnode.cpp b/src/hotspot/share/opto/subnode.cpp index e4e4717bd55..36ede9110cc 100644 --- a/src/hotspot/share/opto/subnode.cpp +++ b/src/hotspot/share/opto/subnode.cpp @@ -683,6 +683,9 @@ const Type* CmpUNode::Value(PhaseGVN* phase) const { if (t2 == TypeInt::INT) { // Compare to bottom? return bottom_type(); } + + const Type* t_sub = sub(t1, t2); // compare based on immediate inputs + uint in1_op = in1->Opcode(); if (in1_op == Op_AddI || in1_op == Op_SubI) { // The problem rise when result of AddI(SubI) may overflow @@ -735,13 +738,15 @@ const Type* CmpUNode::Value(PhaseGVN* phase) const { const TypeInt* tr2 = TypeInt::make(lo_tr2, hi_tr2, w); const TypeInt* cmp1 = sub(tr1, t2)->is_int(); const TypeInt* cmp2 = sub(tr2, t2)->is_int(); - // compute union, so that cmp handles all possible results from the two cases - return cmp1->meet(cmp2); + // Compute union, so that cmp handles all possible results from the two cases + const Type* t_cmp = cmp1->meet(cmp2); + // Pick narrowest type, based on overflow computation and on immediate inputs + return t_sub->filter(t_cmp); } } } - return sub(t1, t2); // Local flavor of type subtraction + return t_sub; } bool CmpUNode::is_index_range_check() const { diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index 06f4914199d..92f2924386a 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -868,7 +868,7 @@ bool LibraryCallKit::inline_vector_mem_operation(bool is_store) { set_result(box); } - old_map->destruct(&_gvn); + destruct_map_clone(old_map); if (can_access_non_heap) { insert_mem_bar(Op_MemBarCPUOrder); @@ -1006,7 +1006,7 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) { set_result(box); } - old_map->destruct(&_gvn); + destruct_map_clone(old_map); C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); return true; diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp index cd011524841..68a29f8bc78 100644 --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -625,7 +625,7 @@ JNI_ENTRY(void, jni_FatalError(JNIEnv *env, const char *msg)) HOTSPOT_JNI_FATALERROR_ENTRY(env, (char *) msg); tty->print_cr("FATAL ERROR in native method: %s", msg); - thread->print_stack(); + thread->print_jni_stack(); os::abort(); // Dump core and abort JNI_END @@ -2234,7 +2234,7 @@ JNI_ENTRY(const char*, jni_GetStringUTFChars(JNIEnv *env, jstring string, jboole if (s_value != NULL) { size_t length = java_lang_String::utf8_length(java_string, s_value); /* JNI Specification states return NULL on OOM */ - result = AllocateHeap(length + 1, mtInternal, 0, AllocFailStrategy::RETURN_NULL); + result = AllocateHeap(length + 1, mtInternal, AllocFailStrategy::RETURN_NULL); if (result != NULL) { java_lang_String::as_utf8_string(java_string, s_value, result, (int) length + 1); if (isCopy != NULL) { diff --git a/src/hotspot/share/prims/jniCheck.cpp b/src/hotspot/share/prims/jniCheck.cpp index 34b05d339b0..52fa413fa1f 100644 --- a/src/hotspot/share/prims/jniCheck.cpp +++ b/src/hotspot/share/prims/jniCheck.cpp @@ -145,7 +145,7 @@ static const char * fatal_non_utf8_class_name2 = "\""; // When in VM state: static void ReportJNIWarning(JavaThread* thr, const char *msg) { tty->print_cr("WARNING in native method: %s", msg); - thr->print_stack(); + thr->print_jni_stack(); } // When in NATIVE state: @@ -196,7 +196,7 @@ check_pending_exception(JavaThread* thr) { IN_VM( tty->print_cr("WARNING in native method: JNI call made without checking exceptions when required to from %s", thr->get_pending_jni_exception_check()); - thr->print_stack(); + thr->print_jni_stack(); ) thr->clear_pending_jni_exception_check(); // Just complain once } diff --git a/src/hotspot/share/prims/jniCheck.hpp b/src/hotspot/share/prims/jniCheck.hpp index d5de95ea8e4..3dc8c5c5939 100644 --- a/src/hotspot/share/prims/jniCheck.hpp +++ b/src/hotspot/share/prims/jniCheck.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,7 @@ extern "C" { // When in VM state: static inline void ReportJNIFatalError(JavaThread* thr, const char *msg) { tty->print_cr("FATAL ERROR in native method: %s", msg); - thr->print_stack(); + thr->print_jni_stack(); os::abort(true); } } diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index 0c8a43680fa..fe9631c08ac 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -2216,6 +2216,7 @@ void JvmtiExport::post_compiled_method_load(JvmtiEnv* env, nmethod *nm) { ResourceMark rm(thread); HandleMark hm(thread); + assert(!nm->is_zombie(), "nmethod zombie in post_compiled_method_load"); // Add inlining information jvmtiCompiledMethodLoadInlineRecord* inlinerecord = create_inline_record(nm); // Pass inlining information through the void pointer diff --git a/src/hotspot/share/prims/jvmtiImpl.cpp b/src/hotspot/share/prims/jvmtiImpl.cpp index ba79e19392c..c542f110122 100644 --- a/src/hotspot/share/prims/jvmtiImpl.cpp +++ b/src/hotspot/share/prims/jvmtiImpl.cpp @@ -960,10 +960,10 @@ JvmtiDeferredEvent JvmtiDeferredEventQueue::dequeue() { } void JvmtiDeferredEventQueue::post(JvmtiEnv* env) { - // Post and destroy queue nodes + // Post events while nmethods are still in the queue and can't be unloaded or made zombie while (_queue_head != NULL) { - JvmtiDeferredEvent event = dequeue(); - event.post_compiled_method_load_event(env); + _queue_head->event().post_compiled_method_load_event(env); + dequeue(); } } diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 296bfe9e49c..f6c947f13e7 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -821,10 +821,9 @@ static bool is_excluded_for_compiler(AbstractCompiler* comp, methodHandle& mh) { return true; } DirectiveSet* directive = DirectivesStack::getMatchingDirective(mh, comp); - if (directive->ExcludeOption) { - return true; - } - return false; + bool exclude = directive->ExcludeOption; + DirectivesStack::release(directive); + return exclude; } static bool can_be_compiled_at_level(methodHandle& mh, jboolean is_osr, int level) { diff --git a/src/hotspot/share/runtime/abstract_vm_version.hpp b/src/hotspot/share/runtime/abstract_vm_version.hpp index fd57a35e287..59c4b299d8e 100644 --- a/src/hotspot/share/runtime/abstract_vm_version.hpp +++ b/src/hotspot/share/runtime/abstract_vm_version.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ typedef enum { NoDetectedVirtualization, XenHVM, + XenPVHVM, // mix-mode on Linux aarch64 KVM, VMWare, HyperV, @@ -71,9 +72,10 @@ class Abstract_VM_Version: AllStatic { static int _vm_build_number; static unsigned int _data_cache_line_flush_size; + public: + static VirtualizationType _detected_virtualization; - public: // Called as part of the runtime services initialization which is // called from the management module initialization (via init_globals()) // after argument parsing and attaching of the main thread has diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 6bac359a778..8c779dcb386 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -700,6 +700,13 @@ const intx ObjectAlignmentInBytes = 8; "MonitorUsedDeflationThreshold is exceeded (0 is off).") \ range(0, max_jint) \ \ + /* notice: the max range value here is max_jint, not max_intx */ \ + /* because of overflow issue */ \ + product(intx, GuaranteedAsyncDeflationInterval, 60000, DIAGNOSTIC, \ + "Async deflate idle monitors every so many milliseconds even " \ + "when MonitorUsedDeflationThreshold is NOT exceeded (0 is off).") \ + range(0, max_jint) \ + \ product(size_t, AvgMonitorsPerThreadEstimate, 1024, DIAGNOSTIC, \ "Used to estimate a variable ceiling based on number of threads " \ "for use with MonitorUsedDeflationThreshold (0 is off).") \ @@ -714,8 +721,9 @@ const intx ObjectAlignmentInBytes = 8; \ product(intx, MonitorUsedDeflationThreshold, 90, DIAGNOSTIC, \ "Percentage of used monitors before triggering deflation (0 is " \ - "off). The check is performed on GuaranteedSafepointInterval " \ - "or AsyncDeflationInterval.") \ + "off). The check is performed on GuaranteedSafepointInterval, " \ + "AsyncDeflationInterval or GuaranteedAsyncDeflationInterval, " \ + "whichever is lower.") \ range(0, 100) \ \ product(uintx, NoAsyncDeflationProgressMax, 3, DIAGNOSTIC, \ @@ -1998,10 +2006,10 @@ const intx ObjectAlignmentInBytes = 8; product(ccstr, ExtraSharedClassListFile, NULL, \ "Extra classlist for building the CDS archive file") \ \ - product(intx, ArchiveRelocationMode, 0, DIAGNOSTIC, \ + product(intx, ArchiveRelocationMode, 1, DIAGNOSTIC, \ "(0) first map at preferred address, and if " \ - "unsuccessful, map at alternative address (default); " \ - "(1) always map at alternative address; " \ + "unsuccessful, map at alternative address; " \ + "(1) always map at alternative address (default); " \ "(2) always map at preferred address, and if unsuccessful, " \ "do not map the archive") \ range(0, 2) \ diff --git a/src/hotspot/share/runtime/monitorDeflationThread.cpp b/src/hotspot/share/runtime/monitorDeflationThread.cpp index 86fda7b8462..4674454ea9b 100644 --- a/src/hotspot/share/runtime/monitorDeflationThread.cpp +++ b/src/hotspot/share/runtime/monitorDeflationThread.cpp @@ -75,6 +75,38 @@ void MonitorDeflationThread::initialize() { } void MonitorDeflationThread::monitor_deflation_thread_entry(JavaThread* jt, TRAPS) { + + // We wait for the lowest of these three intervals: + // - GuaranteedSafepointInterval + // While deflation is not related to safepoint anymore, this keeps compatibility with + // the old behavior when deflation also happened at safepoints. Users who set this + // option to get more/less frequent deflations would be served with this option. + // - AsyncDeflationInterval + // Normal threshold-based deflation heuristic checks the conditions at this interval. + // See is_async_deflation_needed(). + // - GuaranteedAsyncDeflationInterval + // Backup deflation heuristic checks the conditions at this interval. + // See is_async_deflation_needed(). + // + intx wait_time = max_intx; + if (GuaranteedSafepointInterval > 0) { + wait_time = MIN2(wait_time, GuaranteedSafepointInterval); + } + if (AsyncDeflationInterval > 0) { + wait_time = MIN2(wait_time, AsyncDeflationInterval); + } + if (GuaranteedAsyncDeflationInterval > 0) { + wait_time = MIN2(wait_time, GuaranteedAsyncDeflationInterval); + } + + // If all options are disabled, then wait time is not defined, and the deflation + // is effectively disabled. In that case, exit the thread immediately after printing + // a warning message. + if (wait_time == max_intx) { + warning("Async deflation is disabled"); + return; + } + while (true) { { // Need state transition ThreadBlockInVM so that this thread @@ -86,9 +118,7 @@ void MonitorDeflationThread::monitor_deflation_thread_entry(JavaThread* jt, TRAP MonitorLocker ml(MonitorDeflation_lock, Mutex::_no_safepoint_check_flag); while (!ObjectSynchronizer::is_async_deflation_needed()) { // Wait until notified that there is some work to do. - // We wait for GuaranteedSafepointInterval so that - // is_async_deflation_needed() is checked at the same interval. - ml.wait(GuaranteedSafepointInterval); + ml.wait(wait_time); } } diff --git a/src/hotspot/share/runtime/nonJavaThread.cpp b/src/hotspot/share/runtime/nonJavaThread.cpp index b6291c6eabe..2330fd4219a 100644 --- a/src/hotspot/share/runtime/nonJavaThread.cpp +++ b/src/hotspot/share/runtime/nonJavaThread.cpp @@ -270,8 +270,8 @@ void WatcherThread::run() { os::die(); } - // Wait a second, then recheck for timeout. - os::naked_short_sleep(999); + // Wait a bit, then recheck for timeout. + os::naked_short_sleep(250); } } diff --git a/src/hotspot/share/runtime/relocator.cpp b/src/hotspot/share/runtime/relocator.cpp index a55dc644577..dd1c55d8efd 100644 --- a/src/hotspot/share/runtime/relocator.cpp +++ b/src/hotspot/share/runtime/relocator.cpp @@ -413,11 +413,24 @@ void Relocator::adjust_exception_table(int bci, int delta) { } } +static void print_linenumber_table(unsigned char* table) { + CompressedLineNumberReadStream stream(table); + tty->print_cr("-------------------------------------------------"); + while (stream.read_pair()) { + tty->print_cr(" - line %d: %d", stream.line(), stream.bci()); + } + tty->print_cr("-------------------------------------------------"); +} // The width of instruction at "bci" is changing by "delta". Adjust the line number table. void Relocator::adjust_line_no_table(int bci, int delta) { if (method()->has_linenumber_table()) { - CompressedLineNumberReadStream reader(method()->compressed_linenumber_table()); + // if we already made adjustments then use the updated table + unsigned char *table = compressed_line_number_table(); + if (table == nullptr) { + table = method()->compressed_linenumber_table(); + } + CompressedLineNumberReadStream reader(table); CompressedLineNumberWriteStream writer(64); // plenty big for most line number tables while (reader.read_pair()) { int adjustment = (reader.bci() > bci) ? delta : 0; @@ -426,6 +439,10 @@ void Relocator::adjust_line_no_table(int bci, int delta) { writer.write_terminator(); set_compressed_line_number_table(writer.buffer()); set_compressed_line_number_table_size(writer.position()); + if (TraceRelocator) { + tty->print_cr("Adjusted line number table"); + print_linenumber_table(compressed_line_number_table()); + } } } diff --git a/src/hotspot/share/runtime/safepoint.cpp b/src/hotspot/share/runtime/safepoint.cpp index 3191275884c..8cce80ddcbe 100644 --- a/src/hotspot/share/runtime/safepoint.cpp +++ b/src/hotspot/share/runtime/safepoint.cpp @@ -506,19 +506,9 @@ bool SafepointSynchronize::is_cleanup_needed() { return false; } -class ParallelSPCleanupThreadClosure : public ThreadClosure { -public: - void do_thread(Thread* thread) { - if (thread->is_Java_thread()) { - StackWatermarkSet::start_processing(thread->as_Java_thread(), StackWatermarkKind::gc); - } - } -}; - -class ParallelSPCleanupTask : public AbstractGangTask { +class ParallelCleanupTask : public AbstractGangTask { private: SubTasksDone _subtasks; - uint _num_workers; bool _do_lazy_roots; class Tracer { @@ -538,32 +528,14 @@ class ParallelSPCleanupTask : public AbstractGangTask { }; public: - ParallelSPCleanupTask(uint num_workers) : + ParallelCleanupTask() : AbstractGangTask("Parallel Safepoint Cleanup"), _subtasks(SafepointSynchronize::SAFEPOINT_CLEANUP_NUM_TASKS), - _num_workers(num_workers), _do_lazy_roots(!VMThread::vm_operation()->skip_thread_oop_barriers() && Universe::heap()->uses_stack_watermark_barrier()) {} void work(uint worker_id) { - if (_subtasks.try_claim_task(SafepointSynchronize::SAFEPOINT_CLEANUP_LAZY_ROOT_PROCESSING)) { - if (_do_lazy_roots) { - Tracer t("lazy partial thread root processing"); - ParallelSPCleanupThreadClosure cl; - Threads::threads_do(&cl); - } - } - - if (_subtasks.try_claim_task(SafepointSynchronize::SAFEPOINT_CLEANUP_UPDATE_INLINE_CACHES)) { - Tracer t("updating inline caches"); - InlineCacheBuffer::update_inline_caches(); - } - - if (_subtasks.try_claim_task(SafepointSynchronize::SAFEPOINT_CLEANUP_COMPILATION_POLICY)) { - Tracer t("compilation policy safepoint handler"); - CompilationPolicy::do_safepoint_work(); - } - + // These tasks are ordered by relative length of time to execute so that potentially longer tasks start first. if (_subtasks.try_claim_task(SafepointSynchronize::SAFEPOINT_CLEANUP_SYMBOL_TABLE_REHASH)) { if (SymbolTable::needs_rehashing()) { Tracer t("rehashing symbol table"); @@ -585,6 +557,25 @@ class ParallelSPCleanupTask : public AbstractGangTask { } } + if (_subtasks.try_claim_task(SafepointSynchronize::SAFEPOINT_CLEANUP_LAZY_ROOT_PROCESSING)) { + if (_do_lazy_roots) { + Tracer t("lazy partial thread root processing"); + class LazyRootClosure : public ThreadClosure { + public: + void do_thread(Thread* thread) { + StackWatermarkSet::start_processing(thread->as_Java_thread(), StackWatermarkKind::gc); + } + }; + LazyRootClosure cl; + Threads::java_threads_do(&cl); + } + } + + if (_subtasks.try_claim_task(SafepointSynchronize::SAFEPOINT_CLEANUP_UPDATE_INLINE_CACHES)) { + Tracer t("updating inline caches"); + InlineCacheBuffer::update_inline_caches(); + } + if (_subtasks.try_claim_task(SafepointSynchronize::SAFEPOINT_CLEANUP_REQUEST_OOPSTORAGE_CLEANUP)) { // Don't bother reporting event or time for this very short operation. // To have any utility we'd also want to report whether needed. @@ -602,15 +593,13 @@ void SafepointSynchronize::do_cleanup_tasks() { CollectedHeap* heap = Universe::heap(); assert(heap != NULL, "heap not initialized yet?"); + ParallelCleanupTask cleanup; WorkGang* cleanup_workers = heap->safepoint_workers(); if (cleanup_workers != NULL) { // Parallel cleanup using GC provided thread pool. - uint num_cleanup_workers = cleanup_workers->active_workers(); - ParallelSPCleanupTask cleanup(num_cleanup_workers); cleanup_workers->run_task(&cleanup); } else { // Serial cleanup using VMThread. - ParallelSPCleanupTask cleanup(1); cleanup.work(0); } @@ -1011,6 +1000,7 @@ int SafepointTracing::_nof_running = 0; int SafepointTracing::_page_trap = 0; VM_Operation::VMOp_Type SafepointTracing::_current_type; jlong SafepointTracing::_max_sync_time = 0; +jlong SafepointTracing::_max_cleanup_time = 0; jlong SafepointTracing::_max_vmop_time = 0; uint64_t SafepointTracing::_op_count[VM_Operation::VMOp_Terminating] = {0}; @@ -1080,6 +1070,8 @@ void SafepointTracing::statistics_exit_log() { log_info(safepoint, stats)("Maximum sync time " INT64_FORMAT" ns", (int64_t)(_max_sync_time)); + log_info(safepoint, stats)("Maximum cleanup time " INT64_FORMAT" ns", + (int64_t)(_max_cleanup_time)); log_info(safepoint, stats)("Maximum vm operation time (except for Exit VM operation) " INT64_FORMAT " ns", (int64_t)(_max_vmop_time)); @@ -1118,6 +1110,9 @@ void SafepointTracing::end() { if (_max_sync_time < (_last_safepoint_sync_time_ns - _last_safepoint_begin_time_ns)) { _max_sync_time = _last_safepoint_sync_time_ns - _last_safepoint_begin_time_ns; } + if (_max_cleanup_time < (_last_safepoint_cleanup_time_ns - _last_safepoint_sync_time_ns)) { + _max_cleanup_time = _last_safepoint_cleanup_time_ns - _last_safepoint_sync_time_ns; + } if (_max_vmop_time < (_last_safepoint_end_time_ns - _last_safepoint_sync_time_ns)) { _max_vmop_time = _last_safepoint_end_time_ns - _last_safepoint_sync_time_ns; } @@ -1129,14 +1124,16 @@ void SafepointTracing::end() { "Safepoint \"%s\", " "Time since last: " JLONG_FORMAT " ns, " "Reaching safepoint: " JLONG_FORMAT " ns, " + "Cleanup: " JLONG_FORMAT " ns, " "At safepoint: " JLONG_FORMAT " ns, " "Total: " JLONG_FORMAT " ns", VM_Operation::name(_current_type), _last_app_time_ns, - _last_safepoint_cleanup_time_ns - _last_safepoint_begin_time_ns, + _last_safepoint_sync_time_ns - _last_safepoint_begin_time_ns, + _last_safepoint_cleanup_time_ns - _last_safepoint_sync_time_ns, _last_safepoint_end_time_ns - _last_safepoint_cleanup_time_ns, _last_safepoint_end_time_ns - _last_safepoint_begin_time_ns ); - RuntimeService::record_safepoint_end(_last_safepoint_end_time_ns - _last_safepoint_cleanup_time_ns); + RuntimeService::record_safepoint_end(_last_safepoint_end_time_ns - _last_safepoint_sync_time_ns); } diff --git a/src/hotspot/share/runtime/safepoint.hpp b/src/hotspot/share/runtime/safepoint.hpp index c35f55f0c27..95d0e1fb18f 100644 --- a/src/hotspot/share/runtime/safepoint.hpp +++ b/src/hotspot/share/runtime/safepoint.hpp @@ -72,7 +72,6 @@ class SafepointSynchronize : AllStatic { enum SafepointCleanupTasks { SAFEPOINT_CLEANUP_LAZY_ROOT_PROCESSING, SAFEPOINT_CLEANUP_UPDATE_INLINE_CACHES, - SAFEPOINT_CLEANUP_COMPILATION_POLICY, SAFEPOINT_CLEANUP_SYMBOL_TABLE_REHASH, SAFEPOINT_CLEANUP_STRING_TABLE_REHASH, SAFEPOINT_CLEANUP_SYSTEM_DICTIONARY_RESIZE, @@ -261,6 +260,7 @@ class SafepointTracing : public AllStatic { static VM_Operation::VMOp_Type _current_type; static jlong _max_sync_time; + static jlong _max_cleanup_time; static jlong _max_vmop_time; static uint64_t _op_count[VM_Operation::VMOp_Terminating]; diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index b0e0059b16b..869b5604c8f 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -213,6 +213,9 @@ void ObjectSynchronizer::initialize() { } // Start the ceiling with the estimate for one thread. set_in_use_list_ceiling(AvgMonitorsPerThreadEstimate); + + // Start the timer for deflations, so it does not trigger immediately. + _last_async_deflation_time_ns = os::javaTimeNanos(); } MonitorList ObjectSynchronizer::_in_use_list; @@ -241,6 +244,7 @@ bool volatile ObjectSynchronizer::_is_async_deflation_requested = false; bool volatile ObjectSynchronizer::_is_final_audit = false; jlong ObjectSynchronizer::_last_async_deflation_time_ns = 0; static uintx _no_progress_cnt = 0; +static bool _no_progress_skip_increment = false; // =====================> Quick functions @@ -1106,7 +1110,14 @@ static bool monitors_used_above_threshold(MonitorList* list) { // Check if our monitor usage is above the threshold: size_t monitor_usage = (monitors_used * 100LL) / ceiling; - return int(monitor_usage) > MonitorUsedDeflationThreshold; + if (int(monitor_usage) > MonitorUsedDeflationThreshold) { + log_info(monitorinflation)("monitors_used=" SIZE_FORMAT ", ceiling=" SIZE_FORMAT + ", monitor_usage=" SIZE_FORMAT ", threshold=" INTX_FORMAT, + monitors_used, ceiling, monitor_usage, MonitorUsedDeflationThreshold); + return true; + } + + return false; } size_t ObjectSynchronizer::in_use_list_ceiling() { @@ -1128,17 +1139,49 @@ void ObjectSynchronizer::set_in_use_list_ceiling(size_t new_value) { bool ObjectSynchronizer::is_async_deflation_needed() { if (is_async_deflation_requested()) { // Async deflation request. + log_info(monitorinflation)("Async deflation needed: explicit request"); return true; } + + jlong time_since_last = time_since_last_async_deflation_ms(); + if (AsyncDeflationInterval > 0 && - time_since_last_async_deflation_ms() > AsyncDeflationInterval && + time_since_last > AsyncDeflationInterval && monitors_used_above_threshold(&_in_use_list)) { // It's been longer than our specified deflate interval and there // are too many monitors in use. We don't deflate more frequently // than AsyncDeflationInterval (unless is_async_deflation_requested) // in order to not swamp the MonitorDeflationThread. + log_info(monitorinflation)("Async deflation needed: monitors used are above the threshold"); return true; } + + if (GuaranteedAsyncDeflationInterval > 0 && + time_since_last > GuaranteedAsyncDeflationInterval) { + // It's been longer than our specified guaranteed deflate interval. + // We need to clean up the used monitors even if the threshold is + // not reached, to keep the memory utilization at bay when many threads + // touched many monitors. + log_info(monitorinflation)("Async deflation needed: guaranteed interval (" INTX_FORMAT " ms) " + "is greater than time since last deflation (" JLONG_FORMAT " ms)", + GuaranteedAsyncDeflationInterval, time_since_last); + + // If this deflation has no progress, then it should not affect the no-progress + // tracking, otherwise threshold heuristics would think it was triggered, experienced + // no progress, and needs to backoff more aggressively. In this "no progress" case, + // the generic code would bump the no-progress counter, and we compensate for that + // by telling it to skip the update. + // + // If this deflation has progress, then it should let non-progress tracking + // know about this, otherwise the threshold heuristics would kick in, potentially + // experience no-progress due to aggressive cleanup by this deflation, and think + // it is still in no-progress stride. In this "progress" case, the generic code would + // zero the counter, and we allow it to happen. + _no_progress_skip_increment = true; + + return true; + } + return false; } @@ -1533,6 +1576,8 @@ size_t ObjectSynchronizer::deflate_idle_monitors() { if (deflated_count != 0) { _no_progress_cnt = 0; + } else if (_no_progress_skip_increment) { + _no_progress_skip_increment = false; } else { _no_progress_cnt++; } @@ -1621,6 +1666,7 @@ void ObjectSynchronizer::do_final_audit_and_print_stats() { return; } set_is_final_audit(); + log_info(monitorinflation)("Starting the final audit."); if (log_is_enabled(Info, monitorinflation)) { // Do a deflation in order to reduce the in-use monitor population @@ -1630,7 +1676,7 @@ void ObjectSynchronizer::do_final_audit_and_print_stats() { ; // empty } // The other audit_and_print_stats() call is done at the Debug - // level at a safepoint in ObjectSynchronizer::do_safepoint_work(). + // level at a safepoint in SafepointSynchronize::do_cleanup_tasks. ObjectSynchronizer::audit_and_print_stats(true /* on_exit */); } } diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 385f7a3fa99..41b04139cbd 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -2284,6 +2284,25 @@ oop JavaThread::current_park_blocker() { return NULL; } +// Print current stack trace for checked JNI warnings and JNI fatal errors. +// This is the external format, selecting the platform +// as applicable, and allowing for a native-only stack. +void JavaThread::print_jni_stack() { + assert(this == JavaThread::current(), "Can't print stack of other threads"); + if (!has_last_Java_frame()) { + ResourceMark rm(this); + char* buf = NEW_RESOURCE_ARRAY_RETURN_NULL(char, O_BUFLEN); + if (buf == nullptr) { + tty->print_cr("Unable to print native stack - out of memory"); + return; + } + frame f = os::current_frame(); + VMError::print_native_stack(tty, f, this, + buf, O_BUFLEN); + } else { + print_stack_on(tty); + } +} void JavaThread::print_stack_on(outputStream* st) { if (!has_last_Java_frame()) return; diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 8cd0964019f..741e8e45143 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -1428,6 +1428,10 @@ class JavaThread: public Thread { // Print stack trace in external format void print_stack_on(outputStream* st); void print_stack() { print_stack_on(tty); } + // Print current stack trace for checked JNI warnings and JNI fatal errors. + // This is the external format from above, but selecting the platform + // as applicable. + void print_jni_stack(); // Print stack traces in various internal formats void trace_stack() PRODUCT_RETURN; diff --git a/src/hotspot/share/runtime/vmOperation.hpp b/src/hotspot/share/runtime/vmOperation.hpp index 1fe9a60841e..d331a0a0539 100644 --- a/src/hotspot/share/runtime/vmOperation.hpp +++ b/src/hotspot/share/runtime/vmOperation.hpp @@ -59,7 +59,8 @@ template(ParallelGCSystemGC) \ template(G1CollectForAllocation) \ template(G1CollectFull) \ - template(G1Concurrent) \ + template(G1PauseRemark) \ + template(G1PauseCleanup) \ template(G1TryInitiateConcMark) \ template(ZMarkStart) \ template(ZMarkEnd) \ diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 2a38e09e67f..7bf40685cf6 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1598,7 +1598,7 @@ typedef HashtableEntry KlassHashtableEntry; declare_c2_type(MemBarVolatileNode, MemBarNode) \ declare_c2_type(MemBarCPUOrderNode, MemBarNode) \ declare_c2_type(OnSpinWaitNode, MemBarNode) \ - declare_c2_type(BlackholeNode, MemBarNode) \ + declare_c2_type(BlackholeNode, MultiNode) \ declare_c2_type(InitializeNode, MemBarNode) \ declare_c2_type(ThreadLocalNode, Node) \ declare_c2_type(Opaque1Node, Node) \ diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index 51f47421c50..08e08a7f844 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -800,7 +800,8 @@ void JMXStatusDCmd::execute(DCmdSource source, TRAPS) { if (str != NULL) { char* out = java_lang_String::as_utf8_string(str); if (out) { - output()->print_cr("%s", out); + // Avoid using print_cr() because length maybe longer than O_BUFLEN + output()->print_raw_cr(out); return; } } diff --git a/src/hotspot/share/services/heapDumperCompression.cpp b/src/hotspot/share/services/heapDumperCompression.cpp index 41c97261094..222c6e383f2 100644 --- a/src/hotspot/share/services/heapDumperCompression.cpp +++ b/src/hotspot/share/services/heapDumperCompression.cpp @@ -54,10 +54,14 @@ char const* FileWriter::write_buf(char* buf, ssize_t size) { assert(_fd >= 0, "Must be open"); assert(size > 0, "Must write at least one byte"); - ssize_t n = (ssize_t) os::write(_fd, buf, (uint) size); + while (size > 0) { + ssize_t n = os::write(_fd, buf, (uint) size); + if (n <= 0) { + return os::strerror(errno); + } - if (n <= 0) { - return os::strerror(errno); + buf += n; + size -= n; } return NULL; diff --git a/src/hotspot/share/services/memoryManager.cpp b/src/hotspot/share/services/memoryManager.cpp index a2c155b6fca..3f35604c60e 100644 --- a/src/hotspot/share/services/memoryManager.cpp +++ b/src/hotspot/share/services/memoryManager.cpp @@ -170,8 +170,8 @@ void GCStatInfo::clear() { } -GCMemoryManager::GCMemoryManager(const char* name, const char* gc_end_message) : - MemoryManager(name), _gc_end_message(gc_end_message) { +GCMemoryManager::GCMemoryManager(const char* name) : + MemoryManager(name) { _num_collections = 0; _last_gc_stat = NULL; _last_gc_lock = new Mutex(Mutex::leaf, "_last_gc_lock", true, @@ -236,9 +236,11 @@ void GCMemoryManager::gc_begin(bool recordGCBeginTime, bool recordPreGCUsage, // to ensure the current gc stat is placed in _last_gc_stat. void GCMemoryManager::gc_end(bool recordPostGCUsage, bool recordAccumulatedGCTime, - bool recordGCEndTime, bool countCollection, + bool recordGCEndTime, + bool countCollection, GCCause::Cause cause, - bool allMemoryPoolsAffected) { + bool allMemoryPoolsAffected, + const char* message) { if (recordAccumulatedGCTime) { _accumulated_timer.stop(); } @@ -288,7 +290,7 @@ void GCMemoryManager::gc_end(bool recordPostGCUsage, } if (is_notification_enabled()) { - GCNotifier::pushNotification(this, _gc_end_message, GCCause::to_string(cause)); + GCNotifier::pushNotification(this, message, GCCause::to_string(cause)); } } } diff --git a/src/hotspot/share/services/memoryManager.hpp b/src/hotspot/share/services/memoryManager.hpp index ca40425bf3f..1518387ed0e 100644 --- a/src/hotspot/share/services/memoryManager.hpp +++ b/src/hotspot/share/services/memoryManager.hpp @@ -140,11 +140,10 @@ class GCMemoryManager : public MemoryManager { GCStatInfo* _current_gc_stat; int _num_gc_threads; volatile bool _notification_enabled; - const char* _gc_end_message; bool _pool_always_affected_by_gc[MemoryManager::max_num_pools]; public: - GCMemoryManager(const char* name, const char* gc_end_message); + GCMemoryManager(const char* name); ~GCMemoryManager(); void add_pool(MemoryPool* pool); @@ -167,7 +166,7 @@ class GCMemoryManager : public MemoryManager { bool recordAccumulatedGCTime); void gc_end(bool recordPostGCUsage, bool recordAccumulatedGCTime, bool recordGCEndTime, bool countCollection, GCCause::Cause cause, - bool allMemoryPoolsAffected); + bool allMemoryPoolsAffected, const char* message); void reset_gc_stat() { _num_collections = 0; _accumulated_timer.reset(); } diff --git a/src/hotspot/share/services/memoryService.cpp b/src/hotspot/share/services/memoryService.cpp index bb74092b15c..b9edf887b83 100644 --- a/src/hotspot/share/services/memoryService.cpp +++ b/src/hotspot/share/services/memoryService.cpp @@ -182,10 +182,10 @@ void MemoryService::gc_end(GCMemoryManager* manager, bool recordPostGCUsage, bool recordAccumulatedGCTime, bool recordGCEndTime, bool countCollection, GCCause::Cause cause, - bool allMemoryPoolsAffected) { + bool allMemoryPoolsAffected, const char* message) { // register the GC end statistics and memory usage manager->gc_end(recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime, - countCollection, cause, allMemoryPoolsAffected); + countCollection, cause, allMemoryPoolsAffected, message); } bool MemoryService::set_verbose(bool verbose) { @@ -219,6 +219,7 @@ Handle MemoryService::create_MemoryUsage_obj(MemoryUsage usage, TRAPS) { TraceMemoryManagerStats::TraceMemoryManagerStats(GCMemoryManager* gc_memory_manager, GCCause::Cause cause, + const char* end_message, bool allMemoryPoolsAffected, bool recordGCBeginTime, bool recordPreGCUsage, @@ -227,16 +228,17 @@ TraceMemoryManagerStats::TraceMemoryManagerStats(GCMemoryManager* gc_memory_mana bool recordAccumulatedGCTime, bool recordGCEndTime, bool countCollection) { - initialize(gc_memory_manager, cause, allMemoryPoolsAffected, - recordGCBeginTime, recordPreGCUsage, recordPeakUsage, - recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime, - countCollection); + initialize(gc_memory_manager, cause, end_message, + allMemoryPoolsAffected, recordGCBeginTime, recordPreGCUsage, + recordPeakUsage, recordPostGCUsage, recordAccumulatedGCTime, + recordGCEndTime, countCollection); } // for a subclass to create then initialize an instance before invoking // the MemoryService void TraceMemoryManagerStats::initialize(GCMemoryManager* gc_memory_manager, GCCause::Cause cause, + const char* end_message, bool allMemoryPoolsAffected, bool recordGCBeginTime, bool recordPreGCUsage, @@ -246,6 +248,8 @@ void TraceMemoryManagerStats::initialize(GCMemoryManager* gc_memory_manager, bool recordGCEndTime, bool countCollection) { _gc_memory_manager = gc_memory_manager; + _cause = cause; + _end_message = end_message; _allMemoryPoolsAffected = allMemoryPoolsAffected; _recordGCBeginTime = recordGCBeginTime; _recordPreGCUsage = recordPreGCUsage; @@ -254,7 +258,6 @@ void TraceMemoryManagerStats::initialize(GCMemoryManager* gc_memory_manager, _recordAccumulatedGCTime = recordAccumulatedGCTime; _recordGCEndTime = recordGCEndTime; _countCollection = countCollection; - _cause = cause; MemoryService::gc_begin(_gc_memory_manager, _recordGCBeginTime, _recordAccumulatedGCTime, _recordPreGCUsage, _recordPeakUsage); @@ -262,5 +265,6 @@ void TraceMemoryManagerStats::initialize(GCMemoryManager* gc_memory_manager, TraceMemoryManagerStats::~TraceMemoryManagerStats() { MemoryService::gc_end(_gc_memory_manager, _recordPostGCUsage, _recordAccumulatedGCTime, - _recordGCEndTime, _countCollection, _cause, _allMemoryPoolsAffected); + _recordGCEndTime, _countCollection, _cause, _allMemoryPoolsAffected, + _end_message); } diff --git a/src/hotspot/share/services/memoryService.hpp b/src/hotspot/share/services/memoryService.hpp index 3c7bc483dfb..a00e49dd0a7 100644 --- a/src/hotspot/share/services/memoryService.hpp +++ b/src/hotspot/share/services/memoryService.hpp @@ -104,7 +104,7 @@ class MemoryService : public AllStatic { bool recordAccumulatedGCTime, bool recordGCEndTime, bool countCollection, GCCause::Cause cause, - bool allMemoryPoolsAffected); + bool allMemoryPoolsAffected, const char* notificationMessage = nullptr); static bool get_verbose() { return log_is_enabled(Info, gc); } static bool set_verbose(bool verbose); @@ -116,19 +116,21 @@ class MemoryService : public AllStatic { class TraceMemoryManagerStats : public StackObj { private: GCMemoryManager* _gc_memory_manager; - bool _allMemoryPoolsAffected; - bool _recordGCBeginTime; - bool _recordPreGCUsage; - bool _recordPeakUsage; - bool _recordPostGCUsage; - bool _recordAccumulatedGCTime; - bool _recordGCEndTime; - bool _countCollection; - GCCause::Cause _cause; + GCCause::Cause _cause; + const char* _end_message; + bool _allMemoryPoolsAffected; + bool _recordGCBeginTime; + bool _recordPreGCUsage; + bool _recordPeakUsage; + bool _recordPostGCUsage; + bool _recordAccumulatedGCTime; + bool _recordGCEndTime; + bool _countCollection; public: TraceMemoryManagerStats() {} TraceMemoryManagerStats(GCMemoryManager* gc_memory_manager, GCCause::Cause cause, + const char* end_message, bool allMemoryPoolsAffected = true, bool recordGCBeginTime = true, bool recordPreGCUsage = true, @@ -140,6 +142,7 @@ class TraceMemoryManagerStats : public StackObj { void initialize(GCMemoryManager* gc_memory_manager, GCCause::Cause cause, + const char* end_message, bool allMemoryPoolsAffected, bool recordGCBeginTime, bool recordPreGCUsage, diff --git a/src/hotspot/share/utilities/nativeCallStack.hpp b/src/hotspot/share/utilities/nativeCallStack.hpp index 33ce0164d84..8387a1ce259 100644 --- a/src/hotspot/share/utilities/nativeCallStack.hpp +++ b/src/hotspot/share/utilities/nativeCallStack.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -64,7 +64,7 @@ class NativeCallStack : public StackObj { memset(_stack, 0, sizeof(_stack)); } - NativeCallStack(int toSkip); + explicit NativeCallStack(int toSkip); NativeCallStack(address* pc, int frameCount); static inline const NativeCallStack& empty_stack() { return _empty_stack; } diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 5d050693ffd..e9e82ac740e 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2020 SAP SE. All rights reserved. + * Copyright (c) 2023, Red Hat, Inc. and/or its affiliates. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1713,43 +1714,68 @@ void VMError::show_message_box(char *buf, int buflen) { } while (yes); } -// Timeout handling: check if a timeout happened (either a single step did -// timeout or the whole of error reporting hit ErrorLogTimeout). Interrupt -// the reporting thread if that is the case. +// Fatal error handling is subject to several timeouts: +// - a global timeout (controlled via ErrorLogTimeout) +// - local error reporting step timeouts. +// +// The latter aims to "give the JVM a kick" if it gets stuck in one particular place during +// error reporting. This prevents one error reporting step from hogging all the time allotted +// to error reporting under ErrorLogTimeout. +// +// VMError::check_timeout() is called from the watcher thread and checks for either global +// or step timeout. If a timeout happened, we interrupt the reporting thread and set either +// _reporting_did_timeout or _step_did_timeout to signal which timeout fired. Function returns +// true if the *global* timeout fired, which will cause WatcherThread to shut down the JVM +// immediately. bool VMError::check_timeout() { + // This function is supposed to be called from watcher thread during fatal error handling only. + assert(VMError::is_error_reported(), "Only call during error handling"); + assert(Thread::current()->is_Watcher_thread(), "Only call from watcher thread"); + if (ErrorLogTimeout == 0) { return false; } - // Do not check for timeouts if we still have a message box to show to the - // user or if there are OnError handlers to be run. - if (ShowMessageBoxOnError - || (OnError != NULL && OnError[0] != '\0') - || Arguments::abort_hook() != NULL) { - return false; - } + // There are three situations where we suppress the *global* error timeout: + // - if the JVM is embedded and the launcher has its abort hook installed. + // That must be allowed to run. + // - if the user specified one or more OnError commands to run, and these + // did not yet run. These must have finished. + // - if the user (typically developer) specified ShowMessageBoxOnError, + // and the error box has not yet been shown + const bool ignore_global_timeout = + (ShowMessageBoxOnError + || (OnError != nullptr && OnError[0] != '\0') + || Arguments::abort_hook() != nullptr); - const jlong reporting_start_time_l = get_reporting_start_time(); const jlong now = get_current_timestamp(); - // Timestamp is stored in nanos. - if (reporting_start_time_l > 0) { - const jlong end = reporting_start_time_l + (jlong)ErrorLogTimeout * TIMESTAMP_TO_SECONDS_FACTOR; - if (end <= now && !_reporting_did_timeout) { - // We hit ErrorLogTimeout and we haven't interrupted the reporting - // thread yet. - _reporting_did_timeout = true; - interrupt_reporting_thread(); - return true; // global timeout + + // Global timeout hit? + if (!ignore_global_timeout) { + const jlong reporting_start_time = get_reporting_start_time(); + // Timestamp is stored in nanos. + if (reporting_start_time > 0) { + const jlong end = reporting_start_time + (jlong)ErrorLogTimeout * TIMESTAMP_TO_SECONDS_FACTOR; + if (end <= now && !_reporting_did_timeout) { + // We hit ErrorLogTimeout and we haven't interrupted the reporting + // thread yet. + _reporting_did_timeout = true; + interrupt_reporting_thread(); + return true; // global timeout + } } } - const jlong step_start_time_l = get_step_start_time(); - if (step_start_time_l > 0) { + // Reporting step timeout? + const jlong step_start_time = get_step_start_time(); + if (step_start_time > 0) { // A step times out after a quarter of the total timeout. Steps are mostly fast unless they // hang for some reason, so this simple rule allows for three hanging step and still // hopefully leaves time enough for the rest of the steps to finish. - const jlong end = step_start_time_l + (jlong)ErrorLogTimeout * TIMESTAMP_TO_SECONDS_FACTOR / 4; + const int max_step_timeout_secs = 5; + const jlong timeout_duration = MAX2((jlong)max_step_timeout_secs, (jlong)ErrorLogTimeout * TIMESTAMP_TO_SECONDS_FACTOR / 4); + const jlong end = step_start_time + timeout_duration; if (end <= now && !_step_did_timeout) { // The step timed out and we haven't interrupted the reporting // thread yet. diff --git a/src/hotspot/share/utilities/vmError.hpp b/src/hotspot/share/utilities/vmError.hpp index 8a6819d308b..9dfb6db2b9a 100644 --- a/src/hotspot/share/utilities/vmError.hpp +++ b/src/hotspot/share/utilities/vmError.hpp @@ -99,12 +99,6 @@ class VMError : public AllStatic { static void print_stack_trace(outputStream* st, JavaThread* jt, char* buf, int buflen, bool verbose = false); - // public for use by the internal non-product debugger. - NOT_PRODUCT(public:) - static void print_native_stack(outputStream* st, frame fr, Thread* t, - char* buf, int buf_size); - NOT_PRODUCT(private:) - static bool should_report_bug(unsigned int id) { return (id != OOM_MALLOC_ERROR) && (id != OOM_MMAP_ERROR); } @@ -134,6 +128,12 @@ class VMError : public AllStatic { public: + // print_source_info: if true, we try to resolve the source information on platforms that support it + // (useful but may slow down, timeout or misfunction in error situations) + // max_frames: if not -1, overrides StackPrintLimit + static void print_native_stack(outputStream* st, frame fr, Thread* t, + char* buf, int buf_size); + // return a string to describe the error static char* error_string(char* buf, int buflen); diff --git a/src/java.base/linux/classes/jdk/internal/platform/CgroupV1Metrics.java b/src/java.base/linux/classes/jdk/internal/platform/CgroupV1Metrics.java index cf8230b0bfc..c7e83faa59d 100644 --- a/src/java.base/linux/classes/jdk/internal/platform/CgroupV1Metrics.java +++ b/src/java.base/linux/classes/jdk/internal/platform/CgroupV1Metrics.java @@ -53,16 +53,6 @@ public interface CgroupV1Metrics extends Metrics { */ public long getKernelMemoryFailCount(); - /** - * Returns the maximum amount of kernel physical memory, in bytes, that - * can be allocated in the Isolation Group. - * - * @return The maximum amount of memory in bytes or -1 if - * there is no limit set. - * - */ - public long getKernelMemoryLimit(); - /** * Returns the largest amount of kernel physical memory, in bytes, that * have been allocated in the Isolation Group. @@ -93,16 +83,6 @@ public interface CgroupV1Metrics extends Metrics { */ public long getTcpMemoryFailCount(); - /** - * Returns the maximum amount of networking physical memory, in bytes, - * that can be allocated in the Isolation Group. - * - * @return The maximum amount of memory in bytes or -1 if - * there is no limit. - * - */ - public long getTcpMemoryLimit(); - /** * Returns the largest amount of networking physical memory, in bytes, * that have been allocated in the Isolation Group. diff --git a/src/java.base/linux/classes/jdk/internal/platform/CgroupV1MetricsImpl.java b/src/java.base/linux/classes/jdk/internal/platform/CgroupV1MetricsImpl.java index 443b58f1f9d..ce9b0c2cc20 100644 --- a/src/java.base/linux/classes/jdk/internal/platform/CgroupV1MetricsImpl.java +++ b/src/java.base/linux/classes/jdk/internal/platform/CgroupV1MetricsImpl.java @@ -48,11 +48,6 @@ public long getKernelMemoryFailCount() { return metrics.getKernelMemoryFailCount(); } - @Override - public long getKernelMemoryLimit() { - return metrics.getKernelMemoryLimit(); - } - @Override public long getKernelMemoryMaxUsage() { return metrics.getKernelMemoryMaxUsage(); @@ -68,11 +63,6 @@ public long getTcpMemoryFailCount() { return metrics.getTcpMemoryFailCount(); } - @Override - public long getTcpMemoryLimit() { - return metrics.getTcpMemoryLimit(); - } - @Override public long getTcpMemoryMaxUsage() { return metrics.getTcpMemoryMaxUsage(); diff --git a/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java b/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java index 07e9a168ef4..949a8622e56 100644 --- a/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java +++ b/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java @@ -333,10 +333,6 @@ public long getKernelMemoryFailCount() { return getLongValue(memory, "memory.kmem.failcnt"); } - public long getKernelMemoryLimit() { - return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.kmem.limit_in_bytes")); - } - public long getKernelMemoryMaxUsage() { return getLongValue(memory, "memory.kmem.max_usage_in_bytes"); } @@ -349,10 +345,6 @@ public long getTcpMemoryFailCount() { return getLongValue(memory, "memory.kmem.tcp.failcnt"); } - public long getTcpMemoryLimit() { - return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.kmem.tcp.limit_in_bytes")); - } - public long getTcpMemoryMaxUsage() { return getLongValue(memory, "memory.kmem.tcp.max_usage_in_bytes"); } diff --git a/src/java.base/macosx/classes/apple/security/KeychainStore.java b/src/java.base/macosx/classes/apple/security/KeychainStore.java index e4a77e61cca..7cb280035e3 100644 --- a/src/java.base/macosx/classes/apple/security/KeychainStore.java +++ b/src/java.base/macosx/classes/apple/security/KeychainStore.java @@ -69,7 +69,7 @@ static class TrustedCertEntry { Certificate cert; long certRef; // SecCertificateRef for this key - // Each KeyStore.TrustedCertificateEntry have 2 attributes: + // Each KeyStore.TrustedCertificateEntry has 2 attributes: // 1. "trustSettings" -> trustSettings.toString() // 2. "2.16.840.1.113894.746875.1.1" -> trustedKeyUsageValue // The 1st one is mainly for debugging use. The 2nd one is similar @@ -660,7 +660,6 @@ public void engineStore(OutputStream stream, char[] password) _releaseKeychainItemRef(((TrustedCertEntry)entry).certRef); } } else { - Certificate certElem; KeyEntry keyEntry = (KeyEntry)entry; if (keyEntry.chain != null) { @@ -812,8 +811,26 @@ private void createTrustedCertEntry(String alias, List inputTrust, tce.cert = cert; tce.certRef = keychainItemRef; + // Check whether a certificate with same alias already exists and is the same + // If yes, we can return here - the existing entry must have the same + // properties and trust settings + if (entries.contains(alias.toLowerCase())) { + int uniqueVal = 1; + String originalAlias = alias; + var co = entries.get(alias.toLowerCase()); + while (co != null) { + if (co instanceof TrustedCertEntry tco) { + if (tco.cert.equals(tce.cert)) { + return; + } + } + alias = originalAlias + " " + uniqueVal++; + co = entries.get(alias.toLowerCase()); + } + } + tce.trustSettings = new ArrayList<>(); - Map tmpMap = new LinkedHashMap<>(); + Map tmpMap = new LinkedHashMap<>(); for (int i = 0; i < inputTrust.size(); i++) { if (inputTrust.get(i) == null) { tce.trustSettings.add(tmpMap); @@ -836,9 +853,10 @@ private void createTrustedCertEntry(String alias, List inputTrust, } catch (Exception e) { isSelfSigned = false; } + if (tce.trustSettings.isEmpty()) { if (isSelfSigned) { - // If a self-signed certificate has an empty trust settings, + // If a self-signed certificate has trust settings without specific entries, // trust it for all purposes tce.trustedKeyUsageValue = KnownOIDs.anyExtendedKeyUsage.value(); } else { @@ -851,11 +869,19 @@ private void createTrustedCertEntry(String alias, List inputTrust, for (var oneTrust : tce.trustSettings) { var result = oneTrust.get("kSecTrustSettingsResult"); // https://developer.apple.com/documentation/security/sectrustsettingsresult?language=objc - // 1 = kSecTrustSettingsResultTrustRoot, 2 = kSecTrustSettingsResultTrustAsRoot + // 1 = kSecTrustSettingsResultTrustRoot, 2 = kSecTrustSettingsResultTrustAsRoot, + // 3 = kSecTrustSettingsResultDeny // If missing, a default value of kSecTrustSettingsResultTrustRoot is assumed - // for self-signed certificates (see doc for SecTrustSettingsCopyTrustSettings). + // (see doc for SecTrustSettingsCopyTrustSettings). // Note that the same SecPolicyOid can appear in multiple trust settings // for different kSecTrustSettingsAllowedError and/or kSecTrustSettingsPolicyString. + + // If we find explicit distrust in some record, we ignore the certificate + if ("3".equals(result)) { + return; + } + + // Trust, if explicitly trusted or result is null and certificate is self signed if ((result == null && isSelfSigned) || "1".equals(result) || "2".equals(result)) { // When no kSecTrustSettingsPolicy, it means everything @@ -875,20 +901,13 @@ private void createTrustedCertEntry(String alias, List inputTrust, tce.trustedKeyUsageValue = values.toString(); } } + // Make a creation date. if (creationDate != 0) tce.date = new Date(creationDate); else tce.date = new Date(); - int uniqueVal = 1; - String originalAlias = alias; - - while (entries.containsKey(alias.toLowerCase())) { - alias = originalAlias + " " + uniqueVal; - uniqueVal++; - } - entries.put(alias.toLowerCase(), tce); } catch (Exception e) { // The certificate will be skipped. diff --git a/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m b/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m index 3f0c1de962d..b4e19a27995 100644 --- a/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m +++ b/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -285,9 +285,14 @@ static void addIdentitiesToKeystore(JNIEnv *env, jobject keyStore) OSErr searchResult = noErr; jclass jc_KeychainStore = (*env)->FindClass(env, "apple/security/KeychainStore"); - CHECK_NULL(jc_KeychainStore); + if (jc_KeychainStore == NULL) { + goto errOut; + } jmethodID jm_createKeyEntry = (*env)->GetMethodID(env, jc_KeychainStore, "createKeyEntry", "(Ljava/lang/String;JJ[J[[B)V"); - CHECK_NULL(jm_createKeyEntry); + if (jm_createKeyEntry == NULL) { + goto errOut; + } + do { searchResult = SecIdentitySearchCopyNext(identitySearch, &theIdentity); @@ -376,6 +381,35 @@ static void addIdentitiesToKeystore(JNIEnv *env, jobject keyStore) #define ADDNULL(list) (*env)->CallBooleanMethod(env, list, jm_listAdd, NULL) + +static void addTrustSettingsToInputTrust(JNIEnv *env, jmethodID jm_listAdd, CFArrayRef trustSettings, jobject inputTrust) +{ + CFIndex count = CFArrayGetCount(trustSettings); + for (int i = 0; i < count; i++) { + CFDictionaryRef oneTrust = (CFDictionaryRef) CFArrayGetValueAtIndex(trustSettings, i); + CFIndex size = CFDictionaryGetCount(oneTrust); + const void * keys [size]; + const void * values [size]; + CFDictionaryGetKeysAndValues(oneTrust, keys, values); + for (int j = 0; j < size; j++) { + NSString* s = [NSString stringWithFormat:@"%@", keys[j]]; + ADD(inputTrust, s); + s = [NSString stringWithFormat:@"%@", values[j]]; + ADD(inputTrust, s); + } + SecPolicyRef certPolicy; + certPolicy = (SecPolicyRef)CFDictionaryGetValue(oneTrust, kSecTrustSettingsPolicy); + if (certPolicy != NULL) { + CFDictionaryRef policyDict = SecPolicyCopyProperties(certPolicy); + ADD(inputTrust, @"SecPolicyOid"); + NSString* s = [NSString stringWithFormat:@"%@", CFDictionaryGetValue(policyDict, @"SecPolicyOid")]; + ADD(inputTrust, s); + CFRelease(policyDict); + } + ADDNULL(inputTrust); + } +} + static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore) { // Search the user keychain list for all X509 certificates. @@ -385,16 +419,30 @@ static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore) OSErr searchResult = noErr; jclass jc_KeychainStore = (*env)->FindClass(env, "apple/security/KeychainStore"); - CHECK_NULL(jc_KeychainStore); + if (jc_KeychainStore == NULL) { + goto errOut; + } + jmethodID jm_createTrustedCertEntry = (*env)->GetMethodID( env, jc_KeychainStore, "createTrustedCertEntry", "(Ljava/lang/String;Ljava/util/List;JJ[B)V"); - CHECK_NULL(jm_createTrustedCertEntry); + if (jm_createTrustedCertEntry == NULL) { + goto errOut; + } + jclass jc_arrayListClass = (*env)->FindClass(env, "java/util/ArrayList"); - CHECK_NULL(jc_arrayListClass); + if (jc_arrayListClass == NULL) { + goto errOut; + } + jmethodID jm_arrayListCons = (*env)->GetMethodID(env, jc_arrayListClass, "", "()V"); - CHECK_NULL(jm_arrayListCons); + if (jm_arrayListCons == NULL) { + goto errOut; + } + jmethodID jm_listAdd = (*env)->GetMethodID(env, jc_arrayListClass, "add", "(Ljava/lang/Object;)Z"); - CHECK_NULL(jm_listAdd); + if (jm_listAdd == NULL) { + goto errOut; + } do { searchResult = SecKeychainSearchCopyNext(keychainItemSearch, &theItem); @@ -416,43 +464,40 @@ static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore) goto errOut; } - // Only add certificates with trusted settings - CFArrayRef trustSettings; - if (SecTrustSettingsCopyTrustSettings(certRef, kSecTrustSettingsDomainUser, &trustSettings) - == errSecItemNotFound) { - continue; - } - // See KeychainStore::createTrustedCertEntry for content of inputTrust - jobject inputTrust = (*env)->NewObject(env, jc_arrayListClass, jm_arrayListCons); - CHECK_NULL(inputTrust); - - // Dump everything inside trustSettings into inputTrust - CFIndex count = CFArrayGetCount(trustSettings); - for (int i = 0; i < count; i++) { - CFDictionaryRef oneTrust = (CFDictionaryRef) CFArrayGetValueAtIndex(trustSettings, i); - CFIndex size = CFDictionaryGetCount(oneTrust); - const void * keys [size]; - const void * values [size]; - CFDictionaryGetKeysAndValues(oneTrust, keys, values); - for (int j = 0; j < size; j++) { - NSString* s = [NSString stringWithFormat:@"%@", keys[j]]; - ADD(inputTrust, s); - s = [NSString stringWithFormat:@"%@", values[j]]; - ADD(inputTrust, s); + // We load trust settings from domains kSecTrustSettingsDomainUser and kSecTrustSettingsDomainAdmin + // kSecTrustSettingsDomainSystem is ignored because it seems to only contain data for root certificates + jobject inputTrust = NULL; + CFArrayRef trustSettings = NULL; + + // Load user trustSettings into inputTrust + if (SecTrustSettingsCopyTrustSettings(certRef, kSecTrustSettingsDomainUser, &trustSettings) == errSecSuccess && trustSettings != NULL) { + inputTrust = (*env)->NewObject(env, jc_arrayListClass, jm_arrayListCons); + if (inputTrust == NULL) { + CFRelease(trustSettings); + goto errOut; } - SecPolicyRef certPolicy; - certPolicy = (SecPolicyRef)CFDictionaryGetValue(oneTrust, kSecTrustSettingsPolicy); - if (certPolicy != NULL) { - CFDictionaryRef policyDict = SecPolicyCopyProperties(certPolicy); - ADD(inputTrust, @"SecPolicyOid"); - NSString* s = [NSString stringWithFormat:@"%@", CFDictionaryGetValue(policyDict, @"SecPolicyOid")]; - ADD(inputTrust, s); - CFRelease(policyDict); + addTrustSettingsToInputTrust(env, jm_listAdd, trustSettings, inputTrust); + CFRelease(trustSettings); + } + // Load admin trustSettings into inputTrust + trustSettings = NULL; + if (SecTrustSettingsCopyTrustSettings(certRef, kSecTrustSettingsDomainAdmin, &trustSettings) == errSecSuccess && trustSettings != NULL) { + if (inputTrust == NULL) { + inputTrust = (*env)->NewObject(env, jc_arrayListClass, jm_arrayListCons); } - ADDNULL(inputTrust); + if (inputTrust == NULL) { + CFRelease(trustSettings); + goto errOut; + } + addTrustSettingsToInputTrust(env, jm_listAdd, trustSettings, inputTrust); + CFRelease(trustSettings); + } + + // Only add certificates with trust settings + if (inputTrust == NULL) { + continue; } - CFRelease(trustSettings); // Find the creation date. jlong creationDate = getModDateFromItem(env, theItem); diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java index 7ad0f619101..c1caeb18cd1 100644 --- a/src/java.base/share/classes/java/lang/ClassLoader.java +++ b/src/java.base/share/classes/java/lang/ClassLoader.java @@ -406,6 +406,11 @@ private static String nameAndId(ClassLoader ld) { return nid; } + // Returns nameAndId string for exception message printing + String nameAndId() { + return nameAndId; + } + /** * Creates a new class loader of the specified name and using the * specified parent class loader for delegation. diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index 905f61c4b12..de62d82a331 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -660,7 +660,13 @@ public String(byte[] bytes, int offset, int length, Charset charset) { offset = 0; } - int caLen = decodeWithDecoder(cd, ca, bytes, offset, length); + int caLen; + try { + caLen = decodeWithDecoder(cd, ca, bytes, offset, length); + } catch (CharacterCodingException x) { + // Substitution is enabled, so this shouldn't happen + throw new Error(x); + } if (COMPACT_STRINGS) { byte[] bs = StringUTF16.compress(ca, 0, caLen); if (bs != null) { @@ -781,7 +787,13 @@ private static String newStringNoRepl1(byte[] src, Charset cs) { System.getSecurityManager() != null) { src = Arrays.copyOf(src, len); } - int caLen = decodeWithDecoder(cd, ca, src, 0, src.length); + int caLen; + try { + caLen = decodeWithDecoder(cd, ca, src, 0, src.length); + } catch (CharacterCodingException x) { + // throw via IAE + throw new IllegalArgumentException(x); + } if (COMPACT_STRINGS) { byte[] bs = StringUTF16.compress(ca, 0, caLen); if (bs != null) { @@ -835,7 +847,8 @@ private static byte[] encodeWithEncoder(Charset cs, byte coder, byte[] val, bool CharsetEncoder ce = cs.newEncoder(); int len = val.length >> coder; // assume LATIN1=0/UTF16=1; int en = scale(len, ce.maxBytesPerChar()); - if (ce instanceof ArrayEncoder ae) { + // fastpath with ArrayEncoder implies `doReplace`. + if (doReplace && ce instanceof ArrayEncoder ae) { // fastpath for ascii compatible if (coder == LATIN1 && ae.isASCIICompatible() && @@ -846,10 +859,6 @@ private static byte[] encodeWithEncoder(Charset cs, byte coder, byte[] val, bool if (len == 0) { return ba; } - if (doReplace) { - ce.onMalformedInput(CodingErrorAction.REPLACE) - .onUnmappableCharacter(CodingErrorAction.REPLACE); - } int blen = (coder == LATIN1) ? ae.encodeFromLatin1(val, 0, len, ba) : ae.encodeFromUTF16(val, 0, len, ba); @@ -1194,21 +1203,16 @@ private static int decodeUTF8_UTF16(byte[] src, int sp, int sl, byte[] dst, int return dp; } - private static int decodeWithDecoder(CharsetDecoder cd, char[] dst, byte[] src, int offset, int length) { + private static int decodeWithDecoder(CharsetDecoder cd, char[] dst, byte[] src, int offset, int length) + throws CharacterCodingException { ByteBuffer bb = ByteBuffer.wrap(src, offset, length); CharBuffer cb = CharBuffer.wrap(dst, 0, dst.length); - try { - CoderResult cr = cd.decode(bb, cb, true); - if (!cr.isUnderflow()) - cr.throwException(); - cr = cd.flush(cb); - if (!cr.isUnderflow()) - cr.throwException(); - } catch (CharacterCodingException x) { - // Substitution is always enabled, - // so this shouldn't happen - throw new Error(x); - } + CoderResult cr = cd.decode(bb, cb, true); + if (!cr.isUnderflow()) + cr.throwException(); + cr = cd.flush(cb); + if (!cr.isUnderflow()) + cr.throwException(); return cb.position(); } diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 74cc41870ee..c78014a9c95 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2451,6 +2451,10 @@ public long findNative(ClassLoader loader, String entry) { public void exit(int statusCode) { Shutdown.exit(statusCode); } + + public String getLoaderNameID(ClassLoader loader) { + return loader.nameAndId(); + } }); } } diff --git a/src/java.base/share/classes/java/lang/reflect/Proxy.java b/src/java.base/share/classes/java/lang/reflect/Proxy.java index 32aed2448b8..a56f2ddef9d 100644 --- a/src/java.base/share/classes/java/lang/reflect/Proxy.java +++ b/src/java.base/share/classes/java/lang/reflect/Proxy.java @@ -881,7 +881,7 @@ private static void ensureVisible(ClassLoader ld, Class c) { } if (type != c) { throw new IllegalArgumentException(c.getName() + - " referenced from a method is not visible from class loader"); + " referenced from a method is not visible from class loader: " + JLA.getLoaderNameID(ld)); } } diff --git a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template index 4c540d324a0..91ad40b7b7e 100644 --- a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template @@ -134,7 +134,14 @@ class Direct$Type$Buffer$RW$$BO$ } else { address = base; } - cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); + try { + cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); + } catch (Throwable t) { + // Prevent leak if the Deallocator or Cleaner fail for any reason + UNSAFE.freeMemory(base); + Bits.unreserveMemory(size, cap); + throw t; + } att = null; #else[rw] super(cap); diff --git a/src/java.base/share/classes/java/security/Provider.java b/src/java.base/share/classes/java/security/Provider.java index ca8c96c3e04..af8ebeeda57 100644 --- a/src/java.base/share/classes/java/security/Provider.java +++ b/src/java.base/share/classes/java/security/Provider.java @@ -25,6 +25,8 @@ package java.security; +import jdk.internal.event.SecurityProviderServiceEvent; + import java.io.*; import java.util.*; import static java.util.Locale.ENGLISH; @@ -1234,19 +1236,28 @@ public Service getService(String type, String algorithm) { key = new ServiceKey(type, algorithm, false); previousKey = key; } + Service s = null; if (!serviceMap.isEmpty()) { - Service s = serviceMap.get(key); - if (s != null) { - return s; - } + s = serviceMap.get(key); } - synchronized (this) { - ensureLegacyParsed(); - if (legacyMap != null && !legacyMap.isEmpty()) { - return legacyMap.get(key); + if (s == null) { + synchronized (this) { + ensureLegacyParsed(); + if (legacyMap != null && !legacyMap.isEmpty()) { + s = legacyMap.get(key); + } } } - return null; + + if (s != null && SecurityProviderServiceEvent.isTurnedOn()) { + var e = new SecurityProviderServiceEvent(); + e.provider = getName(); + e.type = type; + e.algorithm = algorithm; + e.commit(); + } + + return s; } // ServiceKey from previous getService() call diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index 3a0d7595616..50db24a61a1 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -4321,8 +4321,10 @@ public int parse(DateTimeParseContext context, CharSequence text, int position) if (length >= position + 3 && context.charEquals(text.charAt(position + 2), 'C')) { // There are localized zone texts that start with "UTC", e.g. // "UTC\u221210:00" (MINUS SIGN instead of HYPHEN-MINUS) in French. - // Exclude those ZoneText cases. - if (!(this instanceof ZoneTextPrinterParser)) { + // Exclude those cases. + if (length == position + 3 || + context.charEquals(text.charAt(position + 3), '+') || + context.charEquals(text.charAt(position + 3), '-')) { return parseOffsetBased(context, text, position, position + 3, OffsetIdPrinterParser.INSTANCE_ID_ZERO); } } else { diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java index 323a151b4ce..1f029bc022d 100644 --- a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java +++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java @@ -2759,8 +2759,10 @@ public List> invokeAll(Collection> tasks, ForkJoinTask.cancelIgnoringExceptions(f); else { ((ForkJoinTask)f).awaitPoolInvoke(this, ns); - if ((ns = nanos - (System.nanoTime() - startTime)) < 0L) + if ((ns = nanos - (System.nanoTime() - startTime)) < 0L) { timedOut = true; + ForkJoinTask.cancelIgnoringExceptions(f); + } } } } diff --git a/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java b/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java index de8dd5f8580..7a254ebe39b 100644 --- a/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java +++ b/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java @@ -422,6 +422,10 @@ public int nextInt() { public long nextLong() { return ThreadLocalRandom.current().nextLong(); } + + public double nextDouble() { + return ThreadLocalRandom.current().nextDouble(); + } } /** diff --git a/src/java.base/share/classes/java/util/jar/JarFile.java b/src/java.base/share/classes/java/util/jar/JarFile.java index f0cd0e5c382..bd538649a4f 100644 --- a/src/java.base/share/classes/java/util/jar/JarFile.java +++ b/src/java.base/share/classes/java/util/jar/JarFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import jdk.internal.access.JavaUtilZipFileAccess; import sun.security.action.GetPropertyAction; import sun.security.util.ManifestEntryVerifier; +import sun.security.util.SignatureFileVerifier; import java.io.ByteArrayInputStream; import java.io.EOFException; @@ -151,8 +152,6 @@ public class JarFile extends ZipFile { private static final boolean MULTI_RELEASE_ENABLED; private static final boolean MULTI_RELEASE_FORCED; private static final ThreadLocal isInitializing = new ThreadLocal<>(); - // The maximum size of array to allocate. Some VMs reserve some header words in an array. - private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private SoftReference manRef; private JarEntry manEntry; @@ -800,8 +799,11 @@ private void initializeVerifier() { private byte[] getBytes(ZipEntry ze) throws IOException { try (InputStream is = super.getInputStream(ze)) { long uncompressedSize = ze.getSize(); - if (uncompressedSize > MAX_ARRAY_SIZE) { - throw new IOException("Unsupported size: " + uncompressedSize); + if (uncompressedSize > SignatureFileVerifier.MAX_SIG_FILE_SIZE) { + throw new IOException("Unsupported size: " + uncompressedSize + + " for JarEntry " + ze.getName() + + ". Allowed max size: " + + SignatureFileVerifier.MAX_SIG_FILE_SIZE + " bytes"); } int len = (int)uncompressedSize; int bytesRead; diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index f073fe8fb1b..a2573f83f66 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -69,6 +69,7 @@ import jdk.internal.ref.CleanerFactory; import jdk.internal.vm.annotation.Stable; import sun.nio.cs.UTF_8; +import sun.security.action.GetBooleanAction; import sun.security.util.SignatureFileVerifier; import static java.util.zip.ZipConstants64.*; @@ -121,6 +122,12 @@ public class ZipFile implements ZipConstants, Closeable { */ public static final int OPEN_DELETE = 0x4; + /** + * Flag which specifies whether the validation of the Zip64 extra + * fields should be disabled + */ + private static final boolean disableZip64ExtraFieldValidation = + GetBooleanAction.privilegedGetProperty("jdk.util.zip.disableZip64ExtraFieldValidation"); /** * Opens a zip file for reading. * @@ -1195,6 +1202,16 @@ private int checkAndAddEntry(int pos, int index) if (entryPos + nlen > cen.length - ENDHDR) { zerror("invalid CEN header (bad header size)"); } + + int elen = CENEXT(cen, pos); + if (elen > 0 && !disableZip64ExtraFieldValidation) { + long extraStartingOffset = pos + CENHDR + nlen; + if ((int)extraStartingOffset != extraStartingOffset) { + zerror("invalid CEN header (bad extra offset)"); + } + checkExtraFields(pos, (int)extraStartingOffset, elen); + } + try { ZipCoder zcp = zipCoderForPos(pos); int hash = zcp.checkedHash(cen, entryPos, nlen); @@ -1211,6 +1228,119 @@ private int checkAndAddEntry(int pos, int index) return nlen; } + /** + * Validate the Zip64 Extra block fields + * @param startingOffset Extra Field starting offset within the CEN + * @param extraFieldLen Length of this Extra field + * @throws ZipException If an error occurs validating the Zip64 Extra + * block + */ + private void checkExtraFields(int cenPos, int startingOffset, + int extraFieldLen) throws ZipException { + // Extra field Length cannot exceed 65,535 bytes per the PKWare + // APP.note 4.4.11 + if (extraFieldLen > 0xFFFF) { + zerror("invalid extra field length"); + } + // CEN Offset where this Extra field ends + int extraEndOffset = startingOffset + extraFieldLen; + if (extraEndOffset > cen.length) { + zerror("Invalid CEN header (extra data field size too long)"); + } + int currentOffset = startingOffset; + while (currentOffset < extraEndOffset) { + int tag = get16(cen, currentOffset); + currentOffset += Short.BYTES; + + int tagBlockSize = get16(cen, currentOffset); + int tagBlockEndingOffset = currentOffset + tagBlockSize; + + // The ending offset for this tag block should not go past the + // offset for the end of the extra field + if (tagBlockEndingOffset > extraEndOffset) { + zerror("Invalid CEN header (invalid zip64 extra data field size)"); + } + currentOffset += Short.BYTES; + + if (tag == ZIP64_EXTID) { + // Get the compressed size; + long csize = CENSIZ(cen, cenPos); + // Get the uncompressed size; + long size = CENLEN(cen, cenPos); + checkZip64ExtraFieldValues(currentOffset, tagBlockSize, + csize, size); + } + currentOffset += tagBlockSize; + } + } + + /** + * Validate the Zip64 Extended Information Extra Field (0x0001) block + * size and that the uncompressed size and compressed size field + * values are not negative. + * Note: As we do not use the LOC offset or Starting disk number + * field value we will not validate them + * @param off the starting offset for the Zip64 field value + * @param blockSize the size of the Zip64 Extended Extra Field + * @param csize CEN header compressed size value + * @param size CEN header uncompressed size value + * @throws ZipException if an error occurs + */ + private void checkZip64ExtraFieldValues(int off, int blockSize, long csize, + long size) + throws ZipException { + byte[] cen = this.cen; + // Validate the Zip64 Extended Information Extra Field (0x0001) + // length. + if (!isZip64ExtBlockSizeValid(blockSize)) { + zerror("Invalid CEN header (invalid zip64 extra data field size)"); + } + // Check the uncompressed size is not negative + // Note we do not need to check blockSize is >= 8 as + // we know its length is at least 8 from the call to + // isZip64ExtBlockSizeValid() + if ((size == ZIP64_MAGICVAL)) { + if(get64(cen, off) < 0) { + zerror("Invalid zip64 extra block size value"); + } + } + // Check the compressed size is not negative + if ((csize == ZIP64_MAGICVAL) && (blockSize >= 16)) { + if (get64(cen, off + 8) < 0) { + zerror("Invalid zip64 extra block compressed size value"); + } + } + } + + /** + * Validate the size and contents of a Zip64 extended information field + * The order of the Zip64 fields is fixed, but the fields MUST + * only appear if the corresponding LOC or CEN field is set to 0xFFFF: + * or 0xFFFFFFFF: + * Uncompressed Size - 8 bytes + * Compressed Size - 8 bytes + * LOC Header offset - 8 bytes + * Disk Start Number - 4 bytes + * See PKWare APP.Note Section 4.5.3 for more details + * + * @param blockSize the Zip64 Extended Information Extra Field size + * @return true if the extra block size is valid; false otherwise + */ + private static boolean isZip64ExtBlockSizeValid(int blockSize) { + /* + * As the fields must appear in order, the block size indicates which + * fields to expect: + * 8 - uncompressed size + * 16 - uncompressed size, compressed size + * 24 - uncompressed size, compressed sise, LOC Header offset + * 28 - uncompressed size, compressed sise, LOC Header offset, + * and Disk start number + */ + return switch(blockSize) { + case 8, 16, 24, 28 -> true; + default -> false; + }; + } private int getEntryHash(int index) { return entries[index]; } private int getEntryNext(int index) { return entries[index + 1]; } private int getEntryPos(int index) { return entries[index + 2]; } diff --git a/src/java.base/share/classes/javax/crypto/JceSecurity.java.template b/src/java.base/share/classes/javax/crypto/JceSecurity.java.template index 69ca07dbc6d..7a344e8e306 100644 --- a/src/java.base/share/classes/javax/crypto/JceSecurity.java.template +++ b/src/java.base/share/classes/javax/crypto/JceSecurity.java.template @@ -235,7 +235,11 @@ final class JceSecurity { // return whether this provider is properly signed and can be used by JCE static boolean canUseProvider(Provider p) { - return getVerificationResult(p) == null; + Exception e = getVerificationResult(p); + if (debug != null && e != null) { + debug.println("Provider verification result: " + e); + } + return e == null; } // dummy object to represent null diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index b68490ad7a3..1a76efc206d 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -410,4 +410,10 @@ public interface JavaLangAccess { * @param statusCode the status code */ void exit(int statusCode); + + /** + * Returns '' @ if classloader has a name + * explicitly set otherwise @ + */ + String getLoaderNameID(ClassLoader loader); } diff --git a/src/java.base/share/classes/jdk/internal/event/SecurityProviderServiceEvent.java b/src/java.base/share/classes/jdk/internal/event/SecurityProviderServiceEvent.java new file mode 100644 index 00000000000..2c2c487849b --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/event/SecurityProviderServiceEvent.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.event; + +/** + * Event recording details of Provider.getService(String type, String algorithm) calls + */ + +public final class SecurityProviderServiceEvent extends Event { + private final static SecurityProviderServiceEvent EVENT = new SecurityProviderServiceEvent(); + + /** + * Returns {@code true} if event is enabled, {@code false} otherwise. + */ + public static boolean isTurnedOn() { + return EVENT.isEnabled(); + } + + public String type; + public String algorithm; + public String provider; +} diff --git a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java index 22aa09c9d62..9c88f40e556 100644 --- a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java +++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java @@ -3436,16 +3436,14 @@ public final void loadLoadFence() { * Ensures that stores before the fence will not be reordered with * stores after the fence. * - * @implNote - * This method is operationally equivalent to {@link #storeFence()}. - * * @since 9 */ + @IntrinsicCandidate public final void storeStoreFence() { + // If storeStoreFence intrinsic is not available, fall back to storeFence. storeFence(); } - /** * Throws IllegalAccessError; for use by the VM for access control * error support. diff --git a/src/java.base/share/classes/sun/net/www/HeaderParser.java b/src/java.base/share/classes/sun/net/www/HeaderParser.java index a4b2ab50497..e6d7435a458 100644 --- a/src/java.base/share/classes/sun/net/www/HeaderParser.java +++ b/src/java.base/share/classes/sun/net/www/HeaderParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package sun.net.www; import java.util.Iterator; +import java.util.OptionalInt; /* This is useful for the nightmare of parsing multi-part HTTP/RFC822 headers * sensibly: @@ -246,6 +247,19 @@ public int findInt(String k, int Default) { return Default; } } + + public OptionalInt findInt(String k) { + try { + String s = findValue(k); + if (s == null) { + return OptionalInt.empty(); + } + return OptionalInt.of(Integer.parseInt(s)); + } catch (Throwable t) { + return OptionalInt.empty(); + } + } + /* public static void main(String[] a) throws Exception { System.out.print("enter line to parse> "); diff --git a/src/java.base/share/classes/sun/net/www/http/HttpClient.java b/src/java.base/share/classes/sun/net/www/http/HttpClient.java index a603badb0c6..ba5e38827c7 100644 --- a/src/java.base/share/classes/sun/net/www/http/HttpClient.java +++ b/src/java.base/share/classes/sun/net/www/http/HttpClient.java @@ -29,6 +29,7 @@ import java.net.*; import java.util.Locale; import java.util.Objects; +import java.util.OptionalInt; import java.util.Properties; import java.util.concurrent.locks.ReentrantLock; @@ -127,6 +128,7 @@ private static int getDefaultPort(String proto) { * 0: the server specified no keep alive headers * -1: the server provided "Connection: keep-alive" but did not specify a * a particular time in a "Keep-Alive:" headers + * -2: the server provided "Connection: keep-alive" and timeout=0 * Positive values are the number of seconds specified by the server * in a "Keep-Alive" header */ @@ -897,7 +899,23 @@ private boolean parseHTTPHeader(MessageHeader responses, ProgressSource pi, Http responses.findValue("Keep-Alive")); /* default should be larger in case of proxy */ keepAliveConnections = p.findInt("max", usingProxy?50:5); - keepAliveTimeout = p.findInt("timeout", -1); + if (keepAliveConnections < 0) { + keepAliveConnections = usingProxy?50:5; + } + OptionalInt timeout = p.findInt("timeout"); + if (timeout.isEmpty()) { + keepAliveTimeout = -1; + } else { + keepAliveTimeout = timeout.getAsInt(); + if (keepAliveTimeout < 0) { + // if the server specified a negative (invalid) value + // then we set to -1, which is equivalent to no value + keepAliveTimeout = -1; + } else if (keepAliveTimeout == 0) { + // handled specially to mean close connection immediately + keepAliveTimeout = -2; + } + } } } else if (b[7] != '0') { /* diff --git a/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java b/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java index 6a61932d983..5bdd6f2c009 100644 --- a/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java +++ b/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java @@ -172,6 +172,8 @@ public Void run() { // different default for server and proxy keepAliveTimeout = http.getUsingProxy() ? 60 : 5; } + } else if (keepAliveTimeout == -2) { + keepAliveTimeout = 0; } // at this point keepAliveTimeout is the number of seconds to keep // alive, which could be 0, if the user specified 0 for the property diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java b/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java index c88c178c13e..e563f561d65 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java @@ -528,4 +528,13 @@ private synchronized void writeObject(java.io.ObjectOutputStream s) s2 = new String (pw.getPassword()); s.defaultWriteObject (); } + + /** + * Releases any system or cryptographic resources. + * It is up to implementors to override disposeContext() + * to take necessary action. + */ + public void disposeContext() { + // do nothing + } } diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index 958cf40fc1e..96543280747 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -2029,6 +2029,12 @@ private InputStream getInputStream0() throws IOException { if (serverAuthKey != null) { AuthenticationInfo.endAuthRequest(serverAuthKey); } + if (proxyAuthentication != null) { + proxyAuthentication.disposeContext(); + } + if (serverAuthentication != null) { + serverAuthentication.disposeContext(); + } } } @@ -2274,6 +2280,9 @@ private void doTunneling0() throws IOException { if (proxyAuthKey != null) { AuthenticationInfo.endAuthRequest(proxyAuthKey); } + if (proxyAuthentication != null) { + proxyAuthentication.disposeContext(); + } } // restore original request headers @@ -2525,6 +2534,7 @@ public InetAddress run() } if (ret != null) { if (!ret.setHeaders(this, p, raw)) { + ret.disposeContext(); ret = null; } } @@ -2699,6 +2709,7 @@ private AuthenticationInfo getServerAuthentication(AuthenticationHeader authhdr) if (ret != null ) { if (!ret.setHeaders(this, p, raw)) { + ret.disposeContext(); ret = null; } } @@ -2725,6 +2736,7 @@ private void checkResponseCredentials (boolean inClose) throws IOException { DigestAuthentication da = (DigestAuthentication) currentProxyCredentials; da.checkResponse (raw, method, getRequestURI()); + currentProxyCredentials.disposeContext(); currentProxyCredentials = null; } } @@ -2735,6 +2747,7 @@ private void checkResponseCredentials (boolean inClose) throws IOException { DigestAuthentication da = (DigestAuthentication) currentServerCredentials; da.checkResponse (raw, method, url); + currentServerCredentials.disposeContext(); currentServerCredentials = null; } } diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java b/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java index 9223f3c7b1b..7af71c7d464 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java @@ -245,6 +245,22 @@ private byte[] nextToken(byte[] token) throws IOException { return negotiator.nextToken(token); } + /** + * Releases any system resources and cryptographic information stored in + * the context object and invalidates the context. + */ + @Override + public void disposeContext() { + if (negotiator != null) { + try { + negotiator.disposeContext(); + } catch (IOException ioEx) { + //do not rethrow IOException + } + negotiator = null; + } + } + // MS will send a final WWW-Authenticate even if the status is already // 200 OK. The token can be fed into initSecContext() again to determine // if the server can be trusted. This is not the same concept as Digest's diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/Negotiator.java b/src/java.base/share/classes/sun/net/www/protocol/http/Negotiator.java index 8db213fc0cb..21f83f26db9 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/Negotiator.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/Negotiator.java @@ -82,5 +82,7 @@ private static void finest(Exception e) { logger.finest("NegotiateAuthentication: " + e); } } + + public void disposeContext() throws IOException { }; } diff --git a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java index e216b6a4787..f25002c8d69 100644 --- a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -177,6 +177,11 @@ private void park(FileDescriptor fd, int event, long nanos) throws IOException { millis = -1; } else { millis = NANOSECONDS.toMillis(nanos); + if (nanos > MILLISECONDS.toNanos(millis)) { + // Round up any excess nanos to the nearest millisecond to + // avoid parking for less than requested. + millis++; + } } Net.poll(fd, event, millis); } diff --git a/src/java.base/share/classes/sun/nio/ch/SelChImpl.java b/src/java.base/share/classes/sun/nio/ch/SelChImpl.java index d5102aec314..6917158022e 100644 --- a/src/java.base/share/classes/sun/nio/ch/SelChImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/SelChImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.io.FileDescriptor; import java.io.IOException; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; /** @@ -88,6 +89,11 @@ default void park(int event, long nanos) throws IOException { millis = -1; } else { millis = NANOSECONDS.toMillis(nanos); + if (nanos > MILLISECONDS.toNanos(millis)) { + // Round up any excess nanos to the nearest millisecond to + // avoid parking for less than requested. + millis++; + } } Net.poll(getFD(), event, millis); } diff --git a/src/jdk.charsets/share/classes/sun/nio/cs/ext/GB18030.java.template b/src/java.base/share/classes/sun/nio/cs/GB18030.java similarity index 99% rename from src/jdk.charsets/share/classes/sun/nio/cs/ext/GB18030.java.template rename to src/java.base/share/classes/sun/nio/cs/GB18030.java index 378e4857ba5..362553a5dbb 100644 --- a/src/jdk.charsets/share/classes/sun/nio/cs/ext/GB18030.java.template +++ b/src/java.base/share/classes/sun/nio/cs/GB18030.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ /* */ -package $PACKAGE$; +package sun.nio.cs; import java.nio.ByteBuffer; import java.nio.CharBuffer; @@ -34,7 +34,9 @@ import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; +import jdk.internal.misc.VM; import sun.nio.cs.Surrogate; +import sun.security.action.GetPropertyAction; public class GB18030 extends Charset @@ -43,8 +45,14 @@ public class GB18030 private static final int GB18030_DOUBLE_BYTE = 2; private static final int GB18030_FOUR_BYTE = 3; + // Assumes non-2000 standard if initialized during System.initPhase1(), + // as the system property is not ready to be read in that case. + static final boolean IS_2000 = + VM.initLevel() >= 1 && + "2000".equals(GetPropertyAction.privilegedGetProperty("jdk.charset.GB18030", "")); + public GB18030() { - super("GB18030", $ALIASES$); + super("GB18030", StandardCharsets.aliases_GB18030()); } public boolean contains(Charset cs) { @@ -1046,7 +1054,8 @@ public CharsetEncoder newEncoder() { "\u1E26\u1E27\u1E28\u1E29\u1E2A\u1E2B\u1E2C\u1E2D"+ "\u1E2E\u1E2F\u1E30\u1E31\u1E32\u1E33\u1E34\u1E35"+ "\u1E36\u1E37\u1E38\u1E39\u1E3A\u1E3B\u1E3C\u1E3D"+ - "\u1E3E\u1E3F\u1E40\u1E41\u1E42\u1E43\u1E44\u1E45"+ + (IS_2000 ? "\u1E3E\u1E3F\u1E40\u1E41\u1E42\u1E43\u1E44\u1E45" : + "\u1E3E\uE7C7\u1E40\u1E41\u1E42\u1E43\u1E44\u1E45")+ "\u1E46\u1E47\u1E48\u1E49\u1E4A\u1E4B\u1E4C\u1E4D"+ "\u1E4E\u1E4F\u1E50\u1E51\u1E52\u1E53\u1E54\u1E55"+ "\u1E56\u1E57\u1E58\u1E59\u1E5A\u1E5B\u1E5C\u1E5D"+ @@ -2502,8 +2511,10 @@ public CharsetEncoder newEncoder() { "\u4DF5\u4DF6\u4DF7\u4DF8\u4DF9\u4DFA\u4DFB\u4DFC"+ "\u4DFD\u4DFE\u4DFF\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ - "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ - "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + (IS_2000 ? "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" : + "\uFFFD\uE81E\uE826\uE82B\uE82C\uE832\uE843\uE854"+ + "\uE864\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD")+ "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ @@ -2766,8 +2777,10 @@ public CharsetEncoder newEncoder() { "\uFDF4\uFDF5\uFDF6\uFDF7\uFDF8\uFDF9\uFDFA\uFDFB"+ "\uFDFC\uFDFD\uFDFE\uFDFF\uFE00\uFE01\uFE02\uFE03"+ "\uFE04\uFE05\uFE06\uFE07\uFE08\uFE09\uFE0A\uFE0B"+ - "\uFE0C\uFE0D\uFE0E\uFE0F\uFE10\uFE11\uFE12\uFE13"+ - "\uFE14\uFE15\uFE16\uFE17\uFE18\uFE19\uFE1A\uFE1B"+ + (IS_2000 ? "\uFE0C\uFE0D\uFE0E\uFE0F\uFE10\uFE11\uFE12\uFE13"+ + "\uFE14\uFE15\uFE16\uFE17\uFE18\uFE19\uFE1A\uFE1B" : + "\uFE0C\uFE0D\uFE0E\uFE0F\uE78D\uE78F\uE78E\uE790"+ + "\uE791\uE792\uE793\uE794\uE795\uE796\uFE1A\uFE1B")+ "\uFE1C\uFE1D\uFE1E\uFE1F\uFE20\uFE21\uFE22\uFE23"+ "\uFE24\uFE25\uFE26\uFE27\uFE28\uFE29\uFE2A\uFE2B"+ "\uFE2C\uFE2D\uFE2E\uFE2F\uFE32\uFE45\uFE46\uFE47"+ @@ -3773,10 +3786,14 @@ public CharsetEncoder newEncoder() { "\uE78A\uE78B\uE78C\u03B1\u03B2\u03B3\u03B4\u03B5"+ "\u03B6\u03B7\u03B8\u03B9\u03BA\u03BB\u03BC\u03BD"+ "\u03BE\u03BF\u03C0\u03C1\u03C3\u03C4\u03C5\u03C6"+ - "\u03C7\u03C8\u03C9\uE78D\uE78E\uE78F\uE790\uE791"+ - "\uE792\uE793\uFE35\uFE36\uFE39\uFE3A\uFE3F\uFE40"+ - "\uFE3D\uFE3E\uFE41\uFE42\uFE43\uFE44\uE794\uE795"+ - "\uFE3B\uFE3C\uFE37\uFE38\uFE31\uE796\uFE33\uFE34"+ + (IS_2000 ? "\u03C7\u03C8\u03C9\uE78D\uE78E\uE78F\uE790\uE791"+ + "\uE792\uE793\uFE35\uFE36\uFE39\uFE3A\uFE3F\uFE40"+ + "\uFE3D\uFE3E\uFE41\uFE42\uFE43\uFE44\uE794\uE795"+ + "\uFE3B\uFE3C\uFE37\uFE38\uFE31\uE796\uFE33\uFE34" : + "\u03C7\u03C8\u03C9\uFE10\uFE12\uFE11\uFE13\uFE14"+ + "\uFE15\uFE16\uFE35\uFE36\uFE39\uFE3A\uFE3F\uFE40"+ + "\uFE3D\uFE3E\uFE41\uFE42\uFE43\uFE44\uFE17\uFE18"+ + "\uFE3B\uFE3C\uFE37\uFE38\uFE31\uFE19\uFE33\uFE34")+ "\uE797\uE798\uE799\uE79A\uE79B\uE79C\uE79D\uE79E"+ "\uE79F\uE706\uE707\uE708\uE709\uE70A\uE70B\uE70C"+ "\uE70D\uE70E\uE70F\uE710\uE711\uE712\uE713\uE714"+ @@ -3817,7 +3834,8 @@ public CharsetEncoder newEncoder() { "\uE7C6\u0101\u00E1\u01CE\u00E0\u0113\u00E9\u011B"+ "\u00E8\u012B\u00ED\u01D0\u00EC\u014D\u00F3\u01D2"+ "\u00F2\u016B\u00FA\u01D4\u00F9\u01D6\u01D8\u01DA"+ - "\u01DC\u00FC\u00EA\u0251\uE7C7\u0144\u0148\u01F9"+ + (IS_2000 ? "\u01DC\u00FC\u00EA\u0251\uE7C7\u0144\u0148\u01F9" : + "\u01DC\u00FC\u00EA\u0251\u1E3F\u0144\u0148\u01F9")+ "\u0261\uE7C9\uE7CA\uE7CB\uE7CC\u3105\u3106\u3107"+ "\u3108\u3109\u310A\u310B\u310C\u310D\u310E\u310F"+ "\u3110\u3111\u3112\u3113\u3114\u3115\u3116\u3117"+ @@ -5868,16 +5886,22 @@ public CharsetEncoder newEncoder() { "\uE466\uE467\uFA0C\uFA0D\uFA0E\uFA0F\uFA11\uFA13"+ "\uFA14\uFA18\uFA1F\uFA20\uFA21\uFA23\uFA24\uFA27"+ "\uFA28\uFA29\u2E81\uE816\uE817\uE818\u2E84\u3473"+ - "\u3447\u2E88\u2E8B\uE81E\u359E\u361A\u360E\u2E8C"+ - "\u2E97\u396E\u3918\uE826\u39CF\u39DF\u3A73\u39D0"+ - "\uE82B\uE82C\u3B4E\u3C6E\u3CE0\u2EA7\uE831\uE832"+ + (IS_2000 ? "\u3447\u2E88\u2E8B\uE81E\u359E\u361A\u360E\u2E8C"+ + "\u2E97\u396E\u3918\uE826\u39CF\u39DF\u3A73\u39D0"+ + "\uE82B\uE82C\u3B4E\u3C6E\u3CE0\u2EA7\uE831\uE832" : + "\u3447\u2E88\u2E8B\u9FB4\u359E\u361A\u360E\u2E8C"+ + "\u2E97\u396E\u3918\u9FB5\u39CF\u39DF\u3A73\u39D0"+ + "\u9FB6\u9FB7\u3B4E\u3C6E\u3CE0\u2EA7\uE831\u9FB8")+ "\u2EAA\u4056\u415F\u2EAE\u4337\u2EB3\u2EB6\u2EB7"+ "\uE83B\u43B1\u43AC\u2EBB\u43DD\u44D6\u4661\u464C"+ - "\uE843\uFFFD\u4723\u4729\u477C\u478D\u2ECA\u4947"+ + (IS_2000 ? "\uE843\uFFFD\u4723\u4729\u477C\u478D\u2ECA\u4947" : + "\u9FB9\uFFFD\u4723\u4729\u477C\u478D\u2ECA\u4947")+ "\u497A\u497D\u4982\u4983\u4985\u4986\u499F\u499B"+ - "\u49B7\u49B6\uE854\uE855\u4CA3\u4C9F\u4CA0\u4CA1"+ + (IS_2000 ? "\u49B7\u49B6\uE854\uE855\u4CA3\u4C9F\u4CA0\u4CA1" : + "\u49B7\u49B6\u9FBA\uE855\u4CA3\u4C9F\u4CA0\u4CA1")+ "\u4C77\u4CA2\u4D13\u4D14\u4D15\u4D16\u4D17\u4D18"+ - "\u4D19\u4DAE\uE864\uE468\uE469\uE46A\uE46B\uE46C"+ + (IS_2000 ? "\u4D19\u4DAE\uE864\uE468\uE469\uE46A\uE46B\uE46C" : + "\u4D19\u4DAE\u9FBB\uE468\uE469\uE46A\uE46B\uE46C")+ "\uE46D\uE46E\uE46F\uE470\uE471\uE472\uE473\uE474"+ "\uE475\uE476\uE477\uE478\uE479\uE47A\uE47B\uE47C"+ "\uE47D\uE47E\uE47F\uE480\uE481\uE482\uE483\uE484"+ @@ -6895,7 +6919,8 @@ public CharsetEncoder newEncoder() { "\u3D02\u3D03\u3D04\u3D05\u3D06\u3D07\u3D08\u3D09"+ "\u3D0A\u3D0B\u3D0C\u3D0D\u3D0E\u3D0F\u3D10\u3D11"+ "\u3D12\u3D13\u3D14\u3D15\u3D16\u3D17\u3D18\u3D19"+ - "\u3D1A\u3D1B\u3D1C\u3D1D\u3D1E\u3D1F\u3D20\u3D21"+ + (IS_2000 ? "\u3D1A\u3D1B\u3D1C\u3D1D\u3D1E\u3D1F\u3D20\u3D21" : + "\u3D1A\u3D1B\u3D1C\u3D1D\u3D1E\u3D1F\u3D20\uA8BC")+ "\u3D22\u3D23\u3D24\u3D25\u3D26\u3D27\u3D28\u3D29"+ "\u3D2A\u3D2B\u3D2C\u3D2D\u3D2E\u3D2F\u3D30\u3D31"+ "\u3D32\u3D33\u3D34\u3D35\u3D36\u3D37\u3D38\u3D39"+ @@ -11054,8 +11079,10 @@ public CharsetEncoder newEncoder() { "\uFD93\uC1FA\uB9A8\uEDE8\uFD94\uFD95\uFD96\uB9EA"+ "\uD9DF\uFD97\uFD98\uFD99\uFD9A\uFD9B\u6A63\u6A64"+ "\u6A65\u6A66\u6A67\u6A68\u6A69\u6A6A\u6A6B\u6A6C"+ - "\u6A6D\u6A6E\u6A6F\u6A70\u6A71\u6A72\u6A73\u6A74"+ - "\u6A75\u6A76\u6A77\u6A78\u6A79\u6A7A\u6A7B\u6A7C"+ + (IS_2000 ? "\u6A6D\u6A6E\u6A6F\u6A70\u6A71\u6A72\u6A73\u6A74"+ + "\u6A75\u6A76\u6A77\u6A78\u6A79\u6A7A\u6A7B\u6A7C" : + "\u6A6D\u6A6E\u6A6F\u6A70\uFE59\uFE61\uFE66\uFE67"+ + "\uFE6D\uFE7E\uFE90\uFEA0\u6A79\u6A7A\u6A7B\u6A7C")+ "\u6A7D\u6A7E\u6A7F\u6A80\u6A81\u6A82\u6A83\u6A84"+ "\u6A85\u6A86\u6A87\u6A88\u6A89\u6A8A\u6A8B\u6A8C"+ "\u6A8D\u6A8E\u6A8F\u6A90\u6A91\u6A92\u6A93\u6A94"+ @@ -11467,14 +11494,17 @@ public CharsetEncoder newEncoder() { "\uA2FD\uA2FE\uA4F4\uA4F5\uA4F6\uA4F7\uA4F8\uA4F9"+ "\uA4FA\uA4FB\uA4FC\uA4FD\uA4FE\uA5F7\uA5F8\uA5F9"+ "\uA5FA\uA5FB\uA5FC\uA5FD\uA5FE\uA6B9\uA6BA\uA6BB"+ - "\uA6BC\uA6BD\uA6BE\uA6BF\uA6C0\uA6D9\uA6DA\uA6DB"+ - "\uA6DC\uA6DD\uA6DE\uA6DF\uA6EC\uA6ED\uA6F3\uA6F6"+ + (IS_2000 ? "\uA6BC\uA6BD\uA6BE\uA6BF\uA6C0\uA6D9\uA6DA\uA6DB"+ + "\uA6DC\uA6DD\uA6DE\uA6DF\uA6EC\uA6ED\uA6F3\uA6F6" : + "\uA6BC\uA6BD\uA6BE\uA6BF\uA6C0\u35E7\u35E9\u35E8"+ + "\u35EA\u35EB\u35EC\u35ED\u35EE\u35EF\u35F0\uA6F6")+ "\uA6F7\uA6F8\uA6F9\uA6FA\uA6FB\uA6FC\uA6FD\uA6FE"+ "\uA7C2\uA7C3\uA7C4\uA7C5\uA7C6\uA7C7\uA7C8\uA7C9"+ "\uA7CA\uA7CB\uA7CC\uA7CD\uA7CE\uA7CF\uA7D0\uA7F2"+ "\uA7F3\uA7F4\uA7F5\uA7F6\uA7F7\uA7F8\uA7F9\uA7FA"+ "\uA7FB\uA7FC\uA7FD\uA7FE\uA896\uA897\uA898\uA899"+ - "\uA89A\uA89B\uA89C\uA89D\uA89E\uA89F\uA8A0\uA8BC"+ + (IS_2000 ? "\uA89A\uA89B\uA89C\uA89D\uA89E\uA89F\uA8A0\uA8BC" : + "\uA89A\uA89B\uA89C\uA89D\uA89E\uA89F\uA8A0\u3D21")+ "\u2001\uA8C1\uA8C2\uA8C3\uA8C4\uA8EA\uA8EB\uA8EC"+ "\uA8ED\uA8EE\uA8EF\uA8F0\uA8F1\uA8F2\uA8F3\uA8F4"+ "\uA8F5\uA8F6\uA8F7\uA8F8\uA8F9\uA8FA\uA8FB\uA8FC"+ @@ -11485,16 +11515,23 @@ public CharsetEncoder newEncoder() { "\uA9A3\uA9F0\uA9F1\uA9F2\uA9F3\uA9F4\uA9F5\uA9F6"+ "\uA9F7\uA9F8\uA9F9\uA9FA\uA9FB\uA9FC\uA9FD\uA9FE"+ "\uD7FA\uD7FB\uD7FC\uD7FD\uD7FE\u200F\uFE51\uFE52"+ - "\uFE53\u2010\u2011\u2012\u2013\u2014\uFE59\u2015"+ - "\u2016\u2017\u2018\u2019\u201A\u201B\uFE61\u201C"+ - "\u201D\u201E\u201F\uFE66\uFE67\u2020\u2021\u2022"+ - "\u2023\uFE6C\uFE6D\u2024\u2025\u2026\u2027\u2028"+ + (IS_2000 ? "\uFE53\u2010\u2011\u2012\u2013\u2014\uFE59\u2015"+ + "\u2016\u2017\u2018\u2019\u201A\u201B\uFE61\u201C"+ + "\u201D\u201E\u201F\uFE66\uFE67\u2020\u2021\u2022"+ + "\u2023\uFE6C\uFE6D\u2024\u2025\u2026\u2027\u2028" : + "\uFE53\u2010\u2011\u2012\u2013\u2014\u6A71\u2015"+ + "\u2016\u2017\u2018\u2019\u201A\u201B\u6A72\u201C"+ + "\u201D\u201E\u201F\u6A73\u6A74\u2020\u2021\u2022"+ + "\u2023\uFE6C\u6A75\u2024\u2025\u2026\u2027\u2028")+ "\u2029\u202A\u202B\uFE76\u202C\u202D\u202E\u202F"+ - "\u2030\u2031\u2032\uFE7E\u2033\u2034\u2035\u2036"+ + (IS_2000 ? "\u2030\u2031\u2032\uFE7E\u2033\u2034\u2035\u2036" : + "\u2030\u2031\u2032\u6A76\u2033\u2034\u2035\u2036")+ "\u2037\u2038\u2039\u203A\u203B\u203C\u203D\u203E"+ - "\u203F\u2040\u2041\u2042\uFE90\uFE91\u2043\u2044"+ + (IS_2000 ? "\u203F\u2040\u2041\u2042\uFE90\uFE91\u2043\u2044" : + "\u203F\u2040\u2041\u2042\u6A77\uFE91\u2043\u2044")+ "\u2045\u2046\u2047\u2048\u2049\u204A\u204B\u204C"+ - "\u204D\u204E\u204F\u2050\uFEA0\u2051\u2052\u2053"+ + (IS_2000 ? "\u204D\u204E\u204F\u2050\uFEA0\u2051\u2052\u2053" : + "\u204D\u204E\u204F\u2050\u6A78\u2051\u2052\u2053")+ "\u2054\u2055\u2056\u2057\u2058\u2059\u205A\u205B"+ "\u205C\u205D\u205E\u205F\u2060\u2061\u2062\u2063"+ "\u2064\u2065\u2066\u2067\u2068\u2069\u206A\u206B"+ @@ -12192,8 +12229,10 @@ public CharsetEncoder newEncoder() { "\u24E3\u24E4\u24E5\u24E6\u24E7\u24E8\u24E9\u24EA"+ "\u24EB\u24EC\u24ED\u24EE\u24EF\u24F0\u24F1\u24F2"+ "\u24F3\u24F4\u24F5\u24F6\u24F7\u24F8\u24F9\u24FA"+ - "\u24FB\u24FC\u24FD\u24FE\u24FF\u2500\u2501\u2502"+ - "\u2503\u2504\u2505\u2506\u2507\u2508\u2509\u250A"+ + (IS_2000 ? "\u24FB\u24FC\u24FD\u24FE\u24FF\u2500\u2501\u2502"+ + "\u2503\u2504\u2505\u2506\u2507\u2508\u2509\u250A" : + "\uA6D9\uA6DB\uA6DA\uA6DC\uA6DD\uA6DE\uA6DF\uA6EC"+ + "\uA6ED\uA6F3\u2505\u2506\u2507\u2508\u2509\u250A")+ "\u250B\u250C\u250D\u250E\u250F\u2510\u2511\u2512"+ "\u2513\u2514\u2515\u2516\u2517\u2518\u2519\u251A"+ "\uA955\uA6F2\u251B\uA6F4\uA6F5\uA6E0\uA6E1\uA6F0"+ @@ -12406,8 +12445,13 @@ else if (byte2 == 0x7f || byte2 == 0xff || // BMP Ranges if (offset <= 0x4A62) da[dp++] = getChar(offset); - else if (offset > 0x4A62 && offset <= 0x82BC) - da[dp++] = (char)(offset + 0x5543); + else if (offset > 0x4A62 && offset <= 0x82BC) { + if (offset >= 0x4A71 && offset <= 0x4A78 && !IS_2000) { + da[dp++] = getChar(offset); + } else { + da[dp++] = (char)(offset + 0x5543); + } + } else if (offset >= 0x82BD && offset <= 0x830D) da[dp++] = getChar(offset); else if (offset >= 0x830D && offset <= 0x93A8) @@ -12509,8 +12553,13 @@ else if (byte2 == 0x7f || byte2 == 0xff || // BMP Ranges if (offset <= 0x4A62) dst.put(getChar(offset)); - else if (offset > 0x4A62 && offset <= 0x82BC) - dst.put((char)(offset + 0x5543)); + else if (offset > 0x4A62 && offset <= 0x82BC) { + if (offset >= 0x4A71 && offset <= 0x4A78 && !IS_2000) { + dst.put(getChar(offset)); + } else { + dst.put((char)(offset + 0x5543)); + } + } else if (offset >= 0x82BD && offset <= 0x830D) dst.put(getChar(offset)); else if (offset >= 0x830D && offset <= 0x93A8) @@ -12620,15 +12669,24 @@ else if (c <= 0xA4C6 || c >= 0xE000) { condensedKey = (hiByte - 0x20) * 256 + loByte; - if (c >= 0xE000 && c < 0xF900) + if (c >= 0xE000 && c < 0xF900) { + if (IS_2000) { condensedKey += 0x82BD; + } else { + condensedKey = switch (c) { + case 0xE7C7, 0xE81E, 0xE826, 0xE82B, 0xE82C, 0xE832, + 0xE843, 0xE854, 0xE864 -> condensedKey; + default -> condensedKey + 0x82BD; + }; + } + } else if (c >= 0xF900) - condensedKey += 0x93A9; + condensedKey += 0x93A9; if (hiByte > 0x80) - currentState = GB18030_DOUBLE_BYTE; + currentState = GB18030_DOUBLE_BYTE; else - currentState = GB18030_FOUR_BYTE; + currentState = GB18030_FOUR_BYTE; } else if (c >= 0xA4C7 && c <= 0xD7FF) { condensedKey = c - 0x5543; @@ -12671,7 +12729,7 @@ else if (c >= 0xA4C7 && c <= 0xD7FF) { } sp += inputSize; } - return CoderResult.UNDERFLOW; + return CoderResult.UNDERFLOW; } finally { src.position(sp - src.arrayOffset()); dst.position(dp - dst.arrayOffset()); @@ -12711,15 +12769,24 @@ else if (c <= 0xA4C6 || c >= 0xE000) { condensedKey = (hiByte - 0x20) * 256 + loByte; - if (c >= 0xE000 && c < 0xF900) + if (c >= 0xE000 && c < 0xF900) { + if (IS_2000) { condensedKey += 0x82BD; + } else { + condensedKey = switch (c) { + case 0xE7C7, 0xE81E, 0xE826, 0xE82B, 0xE82C, 0xE832, + 0xE843, 0xE854, 0xE864 -> condensedKey; + default -> condensedKey + 0x82BD; + }; + } + } else if (c >= 0xF900) - condensedKey += 0x93A9; + condensedKey += 0x93A9; if (hiByte > 0x80) - currentState = GB18030_DOUBLE_BYTE; + currentState = GB18030_DOUBLE_BYTE; else - currentState = GB18030_FOUR_BYTE; + currentState = GB18030_FOUR_BYTE; } else if (c >= 0xA4C7 && c <= 0xD7FF) { condensedKey = c - 0x5543; diff --git a/src/java.base/share/classes/sun/nio/cs/StandardCharsets.java.template b/src/java.base/share/classes/sun/nio/cs/StandardCharsets.java.template index b5590880e2d..339e01de39e 100644 --- a/src/java.base/share/classes/sun/nio/cs/StandardCharsets.java.template +++ b/src/java.base/share/classes/sun/nio/cs/StandardCharsets.java.template @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -58,8 +58,13 @@ public class StandardCharsets extends CharsetProvider { } private String canonicalize(String csn) { - String acn = aliasMap().get(csn); - return (acn != null) ? acn : csn; + if (csn.startsWith("gb18030-")) { + return csn.equals("gb18030-2022") && !GB18030.IS_2000 || + csn.equals("gb18030-2000") && GB18030.IS_2000 ? "gb18030" : csn; + } else { + String acn = aliasMap().get(csn); + return (acn != null) ? acn : csn; + } } private Map aliasMap() { diff --git a/src/java.base/share/classes/sun/security/pkcs/PKCS7.java b/src/java.base/share/classes/sun/security/pkcs/PKCS7.java index 0dca814eb76..f7df9934f55 100644 --- a/src/java.base/share/classes/sun/security/pkcs/PKCS7.java +++ b/src/java.base/share/classes/sun/security/pkcs/PKCS7.java @@ -37,6 +37,7 @@ import java.security.*; import java.util.function.Function; +import sun.security.jca.JCAUtil; import sun.security.provider.SHAKE256; import sun.security.timestamp.*; import sun.security.util.*; @@ -67,23 +68,6 @@ public class PKCS7 { private Principal[] certIssuerNames; - /* - * Random number generator for creating nonce values - * (Lazy initialization) - */ - private static class SecureRandomHolder { - static final SecureRandom RANDOM; - static { - SecureRandom tmp = null; - try { - tmp = SecureRandom.getInstance("SHA1PRNG"); - } catch (NoSuchAlgorithmException e) { - // should not happen - } - RANDOM = tmp; - } - } - /** * Unmarshals a PKCS7 block from its encoded form, parsing the * encoded bytes from the InputStream. @@ -1027,11 +1011,9 @@ public static byte[] generateTimestampToken(Timestamper tsa, } // Generate a nonce - BigInteger nonce = null; - if (SecureRandomHolder.RANDOM != null) { - nonce = new BigInteger(64, SecureRandomHolder.RANDOM); - tsQuery.setNonce(nonce); - } + BigInteger nonce = new BigInteger(64, JCAUtil.getDefSecureRandom()); + tsQuery.setNonce(nonce); + tsQuery.requestCertificate(true); TSResponse tsReply = tsa.generateTimestamp(tsQuery); diff --git a/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java b/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java index 859823b937e..98575120ef9 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -267,20 +267,23 @@ public static byte[] getOCSPBytes(List certIds, URI responderURI, out.flush(); } - // Check the response - if (debug != null && - con.getResponseCode() != HttpURLConnection.HTTP_OK) { - debug.println("Received HTTP error: " + con.getResponseCode() - + " - " + con.getResponseMessage()); + // Check the response. Non-200 codes will generate an exception + // but path validation may complete successfully if revocation info + // can be obtained elsewhere (e.g. CRL). + int respCode = con.getResponseCode(); + if (respCode != HttpURLConnection.HTTP_OK) { + String msg = "Received HTTP error: " + respCode + " - " + + con.getResponseMessage(); + if (debug != null) { + debug.println(msg); + } + throw new IOException(msg); } int contentLength = con.getContentLength(); - if (contentLength == -1) { - contentLength = Integer.MAX_VALUE; - } - - return IOUtils.readExactlyNBytes(con.getInputStream(), - contentLength); + return (contentLength == -1) ? con.getInputStream().readAllBytes() : + IOUtils.readExactlyNBytes(con.getInputStream(), + contentLength); } finally { if (con != null) { con.disconnect(); diff --git a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java index d07152eeb18..4ea9255ba0a 100644 --- a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java +++ b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,6 +52,7 @@ import java.util.jar.JarFile; import java.util.jar.Manifest; +import sun.security.action.GetIntegerAction; import sun.security.jca.Providers; import sun.security.pkcs.PKCS7; import sun.security.pkcs.SignerInfo; @@ -97,6 +98,12 @@ public class SignatureFileVerifier { /** ConstraintsParameters for checking disabled algorithms */ private JarConstraintsParameters params; + // the maximum allowed size in bytes for the signature-related files + public static final int MAX_SIG_FILE_SIZE = initializeMaxSigFileSize(); + + // The maximum size of array to allocate. Some VMs reserve some header words in an array. + private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + /** * Create the named SignatureFileVerifier. * @@ -842,4 +849,24 @@ void updateSigners(CodeSigner[] newSigners, signerCache.add(cachedSigners); signers.put(name, cachedSigners); } + + private static int initializeMaxSigFileSize() { + /* + * System property "jdk.jar.maxSignatureFileSize" used to configure + * the maximum allowed number of bytes for the signature-related files + * in a JAR file. + */ + Integer tmp = GetIntegerAction.privilegedGetProperty( + "jdk.jar.maxSignatureFileSize", 8000000); + if (tmp < 0 || tmp > MAX_ARRAY_SIZE) { + if (debug != null) { + debug.println("Default signature file size 8000000 bytes " + + "is used as the specified size for the " + + "jdk.jar.maxSignatureFileSize system property " + + "is out of range: " + tmp); + } + tmp = 8000000; + } + return tmp; + } } diff --git a/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java b/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java index 6f6e190efcd..3762eb820bb 100644 --- a/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java +++ b/src/java.base/share/classes/sun/util/calendar/ZoneInfoFile.java @@ -609,33 +609,16 @@ private static ZoneInfo getZoneInfo(String zoneId, params[9] = toSTZTime[endRule.timeDefinition]; dstSavings = (startRule.offsetAfter - startRule.offsetBefore) * 1000; - // Note: known mismatching -> Asia/Amman + // Note: known mismatching -> Africa/Cairo // ZoneInfo : startDayOfWeek=5 <= Thursday - // startTime=86400000 <= 24 hours - // This: startDayOfWeek=6 - // startTime=0 - // Similar workaround needs to be applied to Africa/Cairo and - // its endDayOfWeek and endTime - // Below is the workarounds, it probably slows down everyone a little - if (params[2] == 6 && params[3] == 0 && - (zoneId.equals("Asia/Amman"))) { - params[2] = 5; - params[3] = 86400000; + // startTime=86400000 <= 24:00 + // This: startDayOfWeek=6 <= Friday + // startTime=0 <= 0:00 + if (zoneId.equals("Africa/Cairo") && + params[7] == Calendar.FRIDAY && params[8] == 0) { + params[7] = Calendar.THURSDAY; + params[8] = SECONDS_PER_DAY * 1000; } - // Additional check for startDayOfWeek=6 and starTime=86400000 - // is needed for Asia/Amman; - if (params[2] == 7 && params[3] == 0 && - (zoneId.equals("Asia/Amman"))) { - params[2] = 6; // Friday - params[3] = 86400000; // 24h - } - //endDayOfWeek and endTime workaround - if (params[7] == 6 && params[8] == 0 && - (zoneId.equals("Africa/Cairo"))) { - params[7] = 5; - params[8] = 86400000; - } - } else if (nTrans > 0) { // only do this if there is something in table already if (lastyear < LASTYEAR) { // ZoneInfo has an ending entry for 2037 @@ -908,7 +891,6 @@ private static class ZoneOffsetTransitionRule { this.dow = dowByte == 0 ? -1 : dowByte; this.secondOfDay = timeByte == 31 ? in.readInt() : timeByte * 3600; this.timeDefinition = (data & (3 << 12)) >>> 12; - this.standardOffset = stdByte == 255 ? in.readInt() : (stdByte - 128) * 900; this.offsetBefore = beforeByte == 3 ? in.readInt() : standardOffset + beforeByte * 1800; this.offsetAfter = afterByte == 3 ? in.readInt() : standardOffset + afterByte * 1800; diff --git a/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java b/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java index d2ade7bb58e..ba71f5bf8ff 100644 --- a/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java +++ b/src/java.base/share/classes/sun/util/cldr/CLDRLocaleProviderAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -238,6 +238,8 @@ public List getCandidateLocales(String baseName, Locale locale) { return applyParentLocales(baseName, candidates); } + private static final Locale NB = Locale.forLanguageTag("nb"); + private static final Locale NO = Locale.forLanguageTag("no"); private List applyParentLocales(String baseName, List candidates) { // check irregular parents for (int i = 0; i < candidates.size(); i++) { @@ -247,11 +249,15 @@ private List applyParentLocales(String baseName, List candidates if (p != null && !candidates.get(i+1).equals(p)) { List applied = candidates.subList(0, i+1); - if (applied.contains(p)) { - // avoid circular recursion (could happen with nb/no case) - continue; + // Tweak for Norwegian locales, CLDR switched the canonical form of + // Norwegian Bokmal language code from "nb" to "no" in CLDR 39 + // (https://unicode-org.atlassian.net/browse/CLDR-2698) + if (p.equals(NB) || p.equals(NO)) { + applied.add(NO); + applied.add(Locale.ROOT); + } else { + applied.addAll(applyParentLocales(baseName, super.getCandidateLocales(baseName, p))); } - applied.addAll(applyParentLocales(baseName, super.getCandidateLocales(baseName, p))); return applied; } } diff --git a/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java b/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java index 6d78d77f64d..f97888a6977 100644 --- a/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java +++ b/src/java.base/share/classes/sun/util/cldr/CLDRTimeZoneNameProviderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -82,7 +82,7 @@ protected String[] getDisplayNameArray(String id, Locale locale) { } if (namesSuper != null) { - // CLDR's resource bundle has an translated entry for this id. + // CLDR's resource bundle has a translated entry for this id. // Fix up names if needed, either missing or no-inheritance namesSuper[INDEX_TZID] = id; @@ -91,7 +91,7 @@ protected String[] getDisplayNameArray(String id, Locale locale) { case "": // Fill in empty elements deriveFallbackName(namesSuper, i, locale, - !TimeZone.getTimeZone(id).useDaylightTime()); + TimeZone.getTimeZone(id).toZoneId().getRules().isFixedOffset()); break; case NO_INHERITANCE_MARKER: // CLDR's "no inheritance marker" @@ -129,7 +129,7 @@ protected String[][] getZoneStrings(Locale locale) { // Derive fallback time zone name according to LDML's logic private void deriveFallbackNames(String[] names, Locale locale) { - boolean noDST = !TimeZone.getTimeZone(names[0]).useDaylightTime(); + boolean noDST = TimeZone.getTimeZone(names[0]).toZoneId().getRules().isFixedOffset(); for (int i = INDEX_STD_LONG; i <= INDEX_GEN_SHORT; i++) { deriveFallbackName(names, i, locale, noDST); @@ -149,13 +149,12 @@ private void deriveFallbackName(String[] names, int index, Locale locale, boolea return; } - // Check parent locale first + // Check parent locales first if (!exists(names, index)) { - CLDRLocaleProviderAdapter clpa = (CLDRLocaleProviderAdapter)LocaleProviderAdapter.forType(Type.CLDR); - var cands = clpa.getCandidateLocales("", locale); - if (cands.size() > 1) { - var parentLoc = cands.get(1); // immediate parent locale - String[] parentNames = super.getDisplayNameArray(id, parentLoc); + var cands = ((CLDRLocaleProviderAdapter)LocaleProviderAdapter.forType(Type.CLDR)) + .getCandidateLocales("", locale); + for (int i = 1; i < cands.size() ; i++) { + String[] parentNames = super.getDisplayNameArray(id, cands.get(i)); if (parentNames != null && !parentNames[index].isEmpty()) { names[index] = parentNames[index]; return; @@ -163,18 +162,20 @@ private void deriveFallbackName(String[] names, int index, Locale locale, boolea } } + // Type Fallback + if (noDST && typeFallback(names, index)) { + return; + } + // Check if COMPAT can substitute the name - if (LocaleProviderAdapter.getAdapterPreference().contains(Type.JRE)) { + if (!exists(names, index) && + LocaleProviderAdapter.getAdapterPreference().contains(Type.JRE)) { String[] compatNames = (String[])LocaleProviderAdapter.forJRE() - .getLocaleResources(mapChineseLocale(locale)) - .getTimeZoneNames(id); + .getLocaleResources(mapChineseLocale(locale)) + .getTimeZoneNames(id); if (compatNames != null) { - for (int i = INDEX_STD_LONG; i <= INDEX_GEN_SHORT; i++) { - // Assumes COMPAT has no empty slots - if (i == index || !exists(names, i)) { - names[i] = compatNames[i]; - } - } + // Assumes COMPAT has no empty slots + names[index] = compatNames[index]; return; } } @@ -184,11 +185,6 @@ private void deriveFallbackName(String[] names, int index, Locale locale, boolea return; } - // Type Fallback - if (noDST && typeFallback(names, index)) { - return; - } - // last resort names[index] = toGMTFormat(id, index == INDEX_DST_LONG || index == INDEX_DST_SHORT, @@ -234,6 +230,11 @@ private boolean typeFallback(String[] names, int index) { } private boolean regionFormatFallback(String[] names, int index, Locale l) { + if (index % 2 == 0) { + // ignore short names + return false; + } + String id = names[INDEX_TZID]; LocaleResources lr = LocaleProviderAdapter.forType(Type.CLDR).getLocaleResources(l); ResourceBundle fd = lr.getJavaTimeFormatData(); diff --git a/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java b/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java index bf7918659ae..2763ac30ca7 100644 --- a/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java +++ b/src/java.base/share/classes/sun/util/resources/TimeZoneNames.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -845,9 +845,7 @@ protected final Object[][] getContents() { {"Europe/Jersey", GMTBST}, {"Europe/Kaliningrad", EET}, {"Europe/Kiev", EET}, - {"Europe/Kirov", new String[] {"Kirov Standard Time", "GMT+03:00", - "Kirov Daylight Time", "GMT+03:00", - "Kirov Time", "GMT+03:00"}}, + {"Europe/Kirov", MSK}, {"Europe/Lisbon", WET}, {"Europe/Ljubljana", CET}, {"Europe/London", GMTBST}, diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index fab52688c04..9be02033877 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -888,7 +888,8 @@ jdk.tls.legacyAlgorithms=NULL, anon, RC4, DES, 3DES_EDE_CBC # Note: This property is currently used by OpenJDK's JSSE implementation. It # is not guaranteed to be examined and used by other implementations. # -jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37 +jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37, \ + ChaCha20-Poly1305 KeyUpdate 2^37 # # Cryptographic Jurisdiction Policy defaults diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java b/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java index a315f863021..08efc2b35a4 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java +++ b/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java @@ -75,10 +75,6 @@ static Path fromUri(UnixFileSystem fs, URI uri) { int pos = 0; while (pos < len) { char c = p.charAt(pos++); - if ((c == '/') && (pos < len) && (p.charAt(pos) == '/')) { - // skip redundant slashes - continue; - } byte b; if (c == '%') { assert (pos+2) <= len; @@ -92,6 +88,10 @@ static Path fromUri(UnixFileSystem fs, URI uri) { throw new IllegalArgumentException("Bad escape"); b = (byte)c; } + if (b == '/' && rlen > 0 && result[rlen-1] == '/') { + // skip redundant slashes + continue; + } result[rlen++] = b; } if (rlen != result.length) diff --git a/src/java.base/unix/native/libnio/ch/Net.c b/src/java.base/unix/native/libnio/ch/Net.c index 42a07359dde..d4a15b34ea6 100644 --- a/src/java.base/unix/native/libnio/ch/Net.c +++ b/src/java.base/unix/native/libnio/ch/Net.c @@ -122,6 +122,51 @@ static jboolean isSourceFilterSupported(){ static jclass isa_class; /* java.net.InetSocketAddress */ static jmethodID isa_ctorID; /* InetSocketAddress(InetAddress, int) */ +static jint handleSocketErrorWithMessage(JNIEnv *env, jint errorValue, + const char* message) +{ + char *xn; + switch (errorValue) { + case EINPROGRESS: /* Non-blocking connect */ + return 0; +#ifdef EPROTO + case EPROTO: + xn = JNU_JAVANETPKG "ProtocolException"; + break; +#endif + case ECONNREFUSED: + case ETIMEDOUT: + case ENOTCONN: + xn = JNU_JAVANETPKG "ConnectException"; + break; + + case EHOSTUNREACH: + xn = JNU_JAVANETPKG "NoRouteToHostException"; + break; + case EADDRINUSE: /* Fall through */ + case EADDRNOTAVAIL: + case EACCES: + xn = JNU_JAVANETPKG "BindException"; + break; + default: + xn = JNU_JAVANETPKG "SocketException"; + break; + } + errno = errorValue; + if (message == NULL) { + JNU_ThrowByNameWithLastError(env, xn, "NioSocketError"); + } else { + JNU_ThrowByNameWithMessageAndLastError(env, xn, message); + } + return IOS_THROWN; +} + +/* Declared in nio_util.h */ +jint handleSocketError(JNIEnv *env, jint errorValue) +{ + return handleSocketErrorWithMessage(env, errorValue, NULL); +} + JNIEXPORT void JNICALL Java_sun_nio_ch_Net_initIDs(JNIEnv *env, jclass clazz) { @@ -615,7 +660,7 @@ Java_sun_nio_ch_Net_joinOrDrop4(JNIEnv *env, jobject this, jboolean join, jobjec if (n < 0) { if (join && (errno == ENOPROTOOPT || errno == EOPNOTSUPP)) return IOS_UNAVAILABLE; - handleSocketError(env, errno); + handleSocketErrorWithMessage(env, errno, "setsockopt failed"); } return 0; } @@ -692,7 +737,7 @@ Java_sun_nio_ch_Net_joinOrDrop6(JNIEnv *env, jobject this, jboolean join, jobjec if (n < 0) { if (join && (errno == ENOPROTOOPT || errno == EOPNOTSUPP)) return IOS_UNAVAILABLE; - handleSocketError(env, errno); + handleSocketErrorWithMessage(env, errno, "setsockopt failed"); } return 0; } @@ -913,38 +958,3 @@ Java_sun_nio_ch_Net_sendOOB(JNIEnv* env, jclass this, jobject fdo, jbyte b) return convertReturnVal(env, n, JNI_FALSE); } -/* Declared in nio_util.h */ - -jint handleSocketError(JNIEnv *env, jint errorValue) -{ - char *xn; - switch (errorValue) { - case EINPROGRESS: /* Non-blocking connect */ - return 0; -#ifdef EPROTO - case EPROTO: - xn = JNU_JAVANETPKG "ProtocolException"; - break; -#endif - case ECONNREFUSED: - case ETIMEDOUT: - case ENOTCONN: - xn = JNU_JAVANETPKG "ConnectException"; - break; - - case EHOSTUNREACH: - xn = JNU_JAVANETPKG "NoRouteToHostException"; - break; - case EADDRINUSE: /* Fall through */ - case EADDRNOTAVAIL: - case EACCES: - xn = JNU_JAVANETPKG "BindException"; - break; - default: - xn = JNU_JAVANETPKG "SocketException"; - break; - } - errno = errorValue; - JNU_ThrowByNameWithLastError(env, xn, "NioSocketError"); - return IOS_THROWN; -} diff --git a/src/java.desktop/macosx/classes/com/apple/eio/FileManager.java b/src/java.desktop/macosx/classes/com/apple/eio/FileManager.java index 2ccdf0eca15..8967a99f15e 100644 --- a/src/java.desktop/macosx/classes/com/apple/eio/FileManager.java +++ b/src/java.desktop/macosx/classes/com/apple/eio/FileManager.java @@ -53,9 +53,13 @@ * * @since 1.4 */ -@SuppressWarnings("removal") public class FileManager { static { + loadOSXLibrary(); + } + + @SuppressWarnings("removal") + private static void loadOSXLibrary() { java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Void run() { @@ -132,6 +136,7 @@ public static int OSTypeToInt(String type) { * @since 1.4 */ public static void setFileTypeAndCreator(String filename, int type, int creator) throws IOException { + @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkWrite(filename); @@ -146,6 +151,7 @@ public static void setFileTypeAndCreator(String filename, int type, int creator) * @since 1.4 */ public static void setFileType(String filename, int type) throws IOException { + @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkWrite(filename); @@ -160,6 +166,7 @@ public static void setFileType(String filename, int type) throws IOException { * @since 1.4 */ public static void setFileCreator(String filename, int creator) throws IOException { + @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkWrite(filename); @@ -174,6 +181,7 @@ public static void setFileCreator(String filename, int creator) throws IOExcepti * @since 1.4 */ public static int getFileType(String filename) throws IOException { + @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(filename); @@ -188,6 +196,7 @@ public static int getFileType(String filename) throws IOException { * @since 1.4 */ public static int getFileCreator(String filename) throws IOException { + @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(filename); @@ -251,6 +260,7 @@ public static String findFolder(short domain, int folderType) throws FileNotFoun * @since 1.4 */ public static String findFolder(short domain, int folderType, boolean createIfNeeded) throws FileNotFoundException { + @SuppressWarnings("removal") final SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(new RuntimePermission("canExamineFileSystem")); @@ -278,6 +288,7 @@ public static String findFolder(short domain, int folderType, boolean createIfNe */ @Deprecated public static void openURL(String url) throws IOException { + @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(new RuntimePermission("canOpenURLs")); @@ -329,6 +340,7 @@ public static String getResource(String resourceName, String subDirName, String private static native String getNativeResourceFromBundle(String resourceName, String subDirName, String type) throws FileNotFoundException; private static String getResourceFromBundle(String resourceName, String subDirName, String type) throws FileNotFoundException { + @SuppressWarnings("removal") final SecurityManager security = System.getSecurityManager(); if (security != null) security.checkPermission(new RuntimePermission("canReadBundle")); @@ -347,6 +359,7 @@ private static String getResourceFromBundle(String resourceName, String subDirNa * @since Java for Mac OS X 10.5 Update 2 - 1.5 */ public static String getPathToApplicationBundle() { + @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security != null) security.checkPermission(new RuntimePermission("canReadBundle")); return getNativePathToApplicationBundle(); @@ -368,6 +381,7 @@ public static boolean moveToTrash(final File file) throws FileNotFoundException if (file == null) throw new FileNotFoundException(); final String fileName = file.getAbsolutePath(); + @SuppressWarnings("removal") final SecurityManager security = System.getSecurityManager(); if (security != null) security.checkDelete(fileName); @@ -391,6 +405,7 @@ public static boolean revealInFinder(final File file) throws FileNotFoundExcepti if (file == null || !file.exists()) throw new FileNotFoundException(); final String fileName = file.getAbsolutePath(); + @SuppressWarnings("removal") final SecurityManager security = System.getSecurityManager(); if (security != null) security.checkRead(fileName); diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaFileView.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaFileView.java index e4e9a383153..d9f9a66e44c 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/AquaFileView.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaFileView.java @@ -34,7 +34,7 @@ import com.apple.laf.AquaUtils.RecyclableSingleton; -@SuppressWarnings({"removal","serial"}) // JDK implementation class +@SuppressWarnings("serial") // JDK implementation class class AquaFileView extends FileView { private static final boolean DEBUG = false; @@ -57,6 +57,11 @@ class AquaFileView extends FileView { static final int kLSItemInfoExtensionIsHidden = 0x00100000; /* Item has a hidden extension*/ static { + loadOSXUILibrary(); + } + + @SuppressWarnings("removal") + private static void loadOSXUILibrary() { java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Void run() { diff --git a/src/java.desktop/macosx/classes/com/apple/laf/ScreenMenu.java b/src/java.desktop/macosx/classes/com/apple/laf/ScreenMenu.java index 66748944d31..4f6e006faf8 100644 --- a/src/java.desktop/macosx/classes/com/apple/laf/ScreenMenu.java +++ b/src/java.desktop/macosx/classes/com/apple/laf/ScreenMenu.java @@ -36,12 +36,17 @@ import sun.lwawt.LWToolkit; import sun.lwawt.macosx.*; -@SuppressWarnings({"removal","serial"}) // JDK implementation class +@SuppressWarnings("serial") // JDK implementation class final class ScreenMenu extends Menu implements ContainerListener, ComponentListener, ScreenMenuPropertyHandler { static { + loadAWTLibrary(); + } + + @SuppressWarnings("removal") + private static void loadAWTLibrary() { java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Void run() { diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java index 4f4578caecd..2f23d8d0fdf 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java @@ -67,12 +67,16 @@ import sun.awt.AWTAccessor; import sun.lwawt.LWWindowPeer; -@SuppressWarnings("removal") class CAccessibility implements PropertyChangeListener { private static Set ignoredRoles; static { - // Need to load the native library for this code. + loadAWTLibrary(); + } + + @SuppressWarnings("removal") + private static void loadAWTLibrary() { + // Need to load the native library for this code. java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Void run() { diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java index b742ac9e38d..1b037510299 100644 --- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java +++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java @@ -131,7 +131,6 @@ final class NamedCursor extends Cursor { /** * Mac OS X Cocoa-based AWT Toolkit. */ -@SuppressWarnings("removal") public final class LWCToolkit extends LWToolkit { // While it is possible to enumerate all mouse devices // and query them for the number of buttons, the code @@ -147,6 +146,7 @@ public final class LWCToolkit extends LWToolkit { static { System.err.flush(); + @SuppressWarnings("removal") ResourceBundle platformResources = java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { @Override @@ -176,20 +176,23 @@ public ResourceBundle run() { if (!GraphicsEnvironment.isHeadless()) { initIDs(); } - inAWT = AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Boolean run() { - return !Boolean.parseBoolean(System.getProperty("javafx.embed.singleThread", "false")); - } - }); } /* * If true we operate in normal mode and nested runloop is executed in JavaRunLoopMode * If false we operate in singleThreaded FX/AWT interop mode and nested loop uses NSDefaultRunLoopMode */ - private static final boolean inAWT; + @SuppressWarnings("removal") + private static final boolean inAWT + = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Boolean run() { + return !Boolean.parseBoolean( + System.getProperty("javafx.embed.singleThread", "false")); + } + }); + @SuppressWarnings("removal") public LWCToolkit() { final String extraButtons = "sun.awt.enableExtraMouseButtons"; AccessController.doPrivileged((PrivilegedAction) () -> { @@ -248,6 +251,7 @@ public static Color getAppleColor(int color) { } // This is only called from native code. + @SuppressWarnings("removal") static void systemColorsChanged() { EventQueue.invokeLater(() -> { AccessController.doPrivileged( (PrivilegedAction) () -> { @@ -586,6 +590,7 @@ public boolean isAlwaysOnTopSupported() { private static final String APPKIT_THREAD_NAME = "AppKit Thread"; // Intended to be called from the LWCToolkit.m only. + @SuppressWarnings("removal") private static void installToolkitThreadInJava() { Thread.currentThread().setName(APPKIT_THREAD_NAME); AccessController.doPrivileged((PrivilegedAction) () -> { diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m index 4f495e31e36..147af2b7a7f 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m @@ -47,10 +47,6 @@ #define k_JAVA_ROBOT_WHEEL_COUNT 1 -#if !defined(kCGBitmapByteOrder32Host) -#define kCGBitmapByteOrder32Host 0 -#endif - // In OS X, left and right mouse button share the same click count. // That is, if one starts clicking the left button rapidly and then // switches to the right button, then the click count will continue @@ -355,7 +351,7 @@ static inline void autoDelay(BOOL isMove) { 8, picWidth * sizeof(jint), picColorSpace, kCGBitmapByteOrder32Host | - kCGImageAlphaPremultipliedFirst); + kCGImageAlphaNoneSkipFirst); CGColorSpaceRelease(picColorSpace); diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/QuartzSurfaceData.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/QuartzSurfaceData.h index d0312504e10..6eac8458f6c 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/awt/QuartzSurfaceData.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/QuartzSurfaceData.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,14 +29,6 @@ #import #import "JNIUtilities.h" -// these flags are not defined on Tiger on PPC, so we need to make them a no-op -#if !defined(kCGBitmapByteOrder32Host) -#define kCGBitmapByteOrder32Host 0 -#endif -#if !defined(kCGBitmapByteOrder16Host) -#define kCGBitmapByteOrder16Host 0 -#endif - // NOTE : Modify the printSurfaceDataDiagnostics API if you change this enum enum SDRenderType { diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java index 4eeb8b06d9b..8e9b08af725 100644 --- a/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java +++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java @@ -615,8 +615,10 @@ else if (size == 124) height = Math.abs(height); } - if (metadata.compression == BI_RGB) { - long imageDataSize = ((long)width * height * (bitsPerPixel / 8)); + if (metadata.compression == BI_RGB && + metadata.paletteSize == 0 && + metadata.bitsPerPixel >= 16) { + long imageDataSize = (((long)width * height * bitsPerPixel) / 8); if (imageDataSize > (bitmapFileSize - bitmapOffset)) { throw new IIOException(I18N.getString("BMPImageReader9")); } diff --git a/src/java.desktop/share/classes/java/awt/EventQueue.java b/src/java.desktop/share/classes/java/awt/EventQueue.java index eec41c454e1..e7d1dcc8286 100644 --- a/src/java.desktop/share/classes/java/awt/EventQueue.java +++ b/src/java.desktop/share/classes/java/awt/EventQueue.java @@ -94,7 +94,6 @@ * * @since 1.1 */ -@SuppressWarnings("removal") public class EventQueue { private static final AtomicInteger threadInitNumber = new AtomicInteger(); @@ -192,8 +191,6 @@ private static final PlatformLogger getEventLog() { return eventLog; } - private static boolean fxAppThreadIsDispatchThread; - static { AWTAccessor.setEventQueueAccessor( new AWTAccessor.EventQueueAccessor() { @@ -230,15 +227,16 @@ public long getMostRecentEventTime(EventQueue eventQueue) { return eventQueue.getMostRecentEventTimeImpl(); } }); - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - fxAppThreadIsDispatchThread = - "true".equals(System.getProperty("javafx.embed.singleThread")); - return null; - } - }); } + @SuppressWarnings("removal") + private static boolean fxAppThreadIsDispatchThread = + AccessController.doPrivileged(new PrivilegedAction() { + public Boolean run() { + return "true".equals(System.getProperty("javafx.embed.singleThread")); + } + }); + /** * Initializes a new instance of {@code EventQueue}. */ @@ -734,8 +732,11 @@ public void run() { } }; + @SuppressWarnings("removal") final AccessControlContext stack = AccessController.getContext(); + @SuppressWarnings("removal") final AccessControlContext srcAcc = getAccessControlContextFrom(src); + @SuppressWarnings("removal") final AccessControlContext eventAcc = event.getAccessControlContext(); if (srcAcc == null) { javaSecurityAccess.doIntersectionPrivilege(action, stack, eventAcc); @@ -750,6 +751,7 @@ public Void run() { } } + @SuppressWarnings("removal") private static AccessControlContext getAccessControlContextFrom(Object src) { return src instanceof Component ? ((Component)src).getAccessControlContext() : diff --git a/src/java.desktop/share/classes/java/awt/Font.java b/src/java.desktop/share/classes/java/awt/Font.java index b13e6e20074..78373aa4f37 100644 --- a/src/java.desktop/share/classes/java/awt/Font.java +++ b/src/java.desktop/share/classes/java/awt/Font.java @@ -2633,8 +2633,10 @@ public Rectangle2D getStringBounds(char [] chars, // quick check for simple text, assume GV ok to use if simple boolean simple = values == null || - (values.getKerning() == 0 && values.getLigatures() == 0 && - values.getBaselineTransform() == null); + (values.getKerning() == 0 + && values.getLigatures() == 0 + && values.getTracking() == 0 + && values.getBaselineTransform() == null); if (simple) { simple = ! FontUtilities.isComplexText(chars, beginIndex, limit); } diff --git a/src/java.desktop/share/classes/javax/print/DocFlavor.java b/src/java.desktop/share/classes/javax/print/DocFlavor.java index 746c5ed0fa6..557a1799a24 100644 --- a/src/java.desktop/share/classes/javax/print/DocFlavor.java +++ b/src/java.desktop/share/classes/javax/print/DocFlavor.java @@ -385,7 +385,6 @@ * * @author Alan Kaminsky */ -@SuppressWarnings("removal") public class DocFlavor implements Serializable, Cloneable { /** @@ -405,13 +404,10 @@ public class DocFlavor implements Serializable, Cloneable { * This is the charset for all the "HOST" pre-defined {@code DocFlavors} in * the executing VM. */ - public static final String hostEncoding; - - static { - hostEncoding = + @SuppressWarnings("removal") + public static final String hostEncoding = java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction("file.encoding")); - } /** * MIME type. diff --git a/src/java.desktop/share/classes/javax/swing/ImageIcon.java b/src/java.desktop/share/classes/javax/swing/ImageIcon.java index 1ddca794779..566e1c8f870 100644 --- a/src/java.desktop/share/classes/javax/swing/ImageIcon.java +++ b/src/java.desktop/share/classes/javax/swing/ImageIcon.java @@ -85,7 +85,7 @@ * @author Lynn Monsanto * @since 1.2 */ -@SuppressWarnings({"removal","serial"}) // Same-version serialization only +@SuppressWarnings("serial") // Same-version serialization only public class ImageIcon implements Icon, Serializable, Accessible { /* Keep references to the filename and location so that * alternate persistence schemes have the option to archive @@ -105,8 +105,27 @@ public class ImageIcon implements Icon, Serializable, Accessible { * It is left for backward compatibility only. * @deprecated since 1.8 */ + @SuppressWarnings("removal") @Deprecated - protected static final Component component; + protected static final Component component + = AccessController.doPrivileged(new PrivilegedAction() { + public Component run() { + try { + final Component component = createNoPermsComponent(); + + // 6482575 - clear the appContext field so as not to leak it + AWTAccessor.getComponentAccessor(). + setAppContext(component, null); + + return component; + } catch (Throwable e) { + // We don't care about component. + // So don't prevent class initialisation. + e.printStackTrace(); + return null; + } + } + }); /** * Do not use this shared media tracker, which is used to load images. @@ -114,30 +133,9 @@ public class ImageIcon implements Icon, Serializable, Accessible { * @deprecated since 1.8 */ @Deprecated - protected static final MediaTracker tracker; - - static { - component = AccessController.doPrivileged(new PrivilegedAction() { - public Component run() { - try { - final Component component = createNoPermsComponent(); - - // 6482575 - clear the appContext field so as not to leak it - AWTAccessor.getComponentAccessor(). - setAppContext(component, null); - - return component; - } catch (Throwable e) { - // We don't care about component. - // So don't prevent class initialisation. - e.printStackTrace(); - return null; - } - } - }); - tracker = new MediaTracker(component); - } + protected static final MediaTracker tracker = new MediaTracker(component); + @SuppressWarnings("removal") private static Component createNoPermsComponent() { // 7020198 - set acc field to no permissions and no subject // Note, will have appContext set. diff --git a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java index 639c640862d..e4eb45c28a9 100644 --- a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java +++ b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java @@ -101,7 +101,7 @@ */ @JavaBean(defaultProperty = "UI", description = "A small window that pops up and displays a series of choices.") @SwingContainer(false) -@SuppressWarnings({"removal","serial"}) +@SuppressWarnings("serial") public class JPopupMenu extends JComponent implements Accessible,MenuElement { /** @@ -117,14 +117,11 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { new StringBuffer("JPopupMenu.defaultLWPopupEnabledKey"); /** Bug#4425878-Property javax.swing.adjustPopupLocationToFit introduced */ - static boolean popupPostionFixDisabled = false; - - static { - popupPostionFixDisabled = java.security.AccessController.doPrivileged( + @SuppressWarnings("removal") + static boolean popupPostionFixDisabled = + java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction( - "javax.swing.adjustPopupLocationToFit","")).equals("false"); - - } + "javax.swing.adjustPopupLocationToFit","")).equals("false"); transient Component invoker; transient Popup popup; diff --git a/src/java.desktop/share/classes/javax/swing/JRootPane.java b/src/java.desktop/share/classes/javax/swing/JRootPane.java index 0a2e0c3a89a..e3eb7ac75a4 100644 --- a/src/java.desktop/share/classes/javax/swing/JRootPane.java +++ b/src/java.desktop/share/classes/javax/swing/JRootPane.java @@ -199,7 +199,7 @@ * @since 1.2 */ /// PENDING(klobad) Who should be opaque in this component? -@SuppressWarnings({"removal","serial"}) +@SuppressWarnings("serial") public class JRootPane extends JComponent implements Accessible { private static final String uiClassID = "RootPaneUI"; @@ -208,13 +208,19 @@ public class JRootPane extends JComponent implements Accessible { * Whether or not we should dump the stack when true double buffering * is disabled. Default is false. */ - private static final boolean LOG_DISABLE_TRUE_DOUBLE_BUFFERING; + @SuppressWarnings("removal") + private static final boolean LOG_DISABLE_TRUE_DOUBLE_BUFFERING + = AccessController.doPrivileged(new GetBooleanAction( + "swing.logDoubleBufferingDisable")); /** * Whether or not we should ignore requests to disable true double * buffering. Default is false. */ - private static final boolean IGNORE_DISABLE_TRUE_DOUBLE_BUFFERING; + @SuppressWarnings("removal") + private static final boolean IGNORE_DISABLE_TRUE_DOUBLE_BUFFERING + = AccessController.doPrivileged(new GetBooleanAction( + "swing.ignoreDoubleBufferingDisable")); /** * Constant used for the windowDecorationStyle property. Indicates that @@ -326,15 +332,6 @@ public class JRootPane extends JComponent implements Accessible { */ boolean useTrueDoubleBuffering = true; - static { - LOG_DISABLE_TRUE_DOUBLE_BUFFERING = - AccessController.doPrivileged(new GetBooleanAction( - "swing.logDoubleBufferingDisable")); - IGNORE_DISABLE_TRUE_DOUBLE_BUFFERING = - AccessController.doPrivileged(new GetBooleanAction( - "swing.ignoreDoubleBufferingDisable")); - } - /** * Creates a JRootPane, setting up its * glassPane, layeredPane, diff --git a/src/java.desktop/share/classes/javax/swing/SortingFocusTraversalPolicy.java b/src/java.desktop/share/classes/javax/swing/SortingFocusTraversalPolicy.java index 4eb8fef4200..5305be37e86 100644 --- a/src/java.desktop/share/classes/javax/swing/SortingFocusTraversalPolicy.java +++ b/src/java.desktop/share/classes/javax/swing/SortingFocusTraversalPolicy.java @@ -59,7 +59,6 @@ * @see java.util.Comparator * @since 1.4 */ -@SuppressWarnings("removal") public class SortingFocusTraversalPolicy extends InternalFrameFocusTraversalPolicy { @@ -96,12 +95,10 @@ public class SortingFocusTraversalPolicy * When false, the default (tim-sort) algo is used, which may lead to an exception. * See: JDK-8048887 */ - private static final boolean legacySortingFTPEnabled; - - static { - legacySortingFTPEnabled = "true".equals(AccessController.doPrivileged( - new GetPropertyAction("swing.legacySortingFTPEnabled", "true"))); - } + @SuppressWarnings("removal") + private static final boolean legacySortingFTPEnabled = "true".equals( + AccessController.doPrivileged( + new GetPropertyAction("swing.legacySortingFTPEnabled", "true"))); /** * Constructs a SortingFocusTraversalPolicy without a Comparator. diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java index 52c7ab078c3..910d13668d2 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java @@ -615,6 +615,18 @@ public void load(InputStream input, Class resourceBase) throws * new URL(synthFile, path). Refer to * Synth File Format for more * information. + *

+ * Whilst this API may be safe for loading local resources that are + * delivered with a {@code LookAndFeel} or application, and so have an + * equal level of trust with application code, using it to load from + * remote resources, particularly any which may have a lower level of + * trust, is strongly discouraged. + * The alternative mechanisms to load styles from an {@code InputStream} + * {@linkplain #load(InputStream, Class)} + * using resources co-located with the application or by providing a + * {@code SynthStyleFactory} to + * {@linkplain #setStyleFactory setStyleFactory(SynthStyleFactory)} + * are preferred. * * @param url the URL to load the set of * SynthStyle from diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/doc-files/synthFileFormat.html b/src/java.desktop/share/classes/javax/swing/plaf/synth/doc-files/synthFileFormat.html index 8eb1bbda9c1..7d63fa16dca 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/doc-files/synthFileFormat.html +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/doc-files/synthFileFormat.html @@ -70,6 +70,8 @@

File Format

This example loads the look and feel from an input stream, using the specified class as the resource base to resolve paths. +

+

It is also possible to load a look and feel from an arbitrary URL as in the following example.

@@ -94,6 +96,11 @@

File Format

  • Remote JAR file, e.g. jar:http://host/synth-laf.jar!/laf.xml
  • +

    Note: Synth's file format allows for the definition of code to be executed. + Loading any code from a remote location should be used only + with extreme caution from a trusted source over a secure connection. + It is strongly discouraged for an application or a LookAndFeel to do so. +

    While the DTD for synth is specified, the parser is not validating. Parsing will fail only if a necessary attribute is not diff --git a/src/java.desktop/share/classes/sun/awt/FontConfiguration.java b/src/java.desktop/share/classes/sun/awt/FontConfiguration.java index 0363cced013..48fb0b5b667 100644 --- a/src/java.desktop/share/classes/sun/awt/FontConfiguration.java +++ b/src/java.desktop/share/classes/sun/awt/FontConfiguration.java @@ -1252,15 +1252,24 @@ public String getFileNameFromPlatformName(String platformName) { return filenamesMap.get(platformName); } + private static final String fontconfigErrorMessage = + "Fontconfig head is null, check your fonts or fonts configuration"; + /** * Returns a configuration specific path to be appended to the font * search path. */ public String getExtraFontPath() { + if (head == null) { + throw new RuntimeException(fontconfigErrorMessage); + } return getString(head[INDEX_appendedfontpath]); } public String getVersion() { + if (head == null) { + throw new RuntimeException(fontconfigErrorMessage); + } return getString(head[INDEX_version]); } diff --git a/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java b/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java index fe0fd6e787d..bb327889a86 100644 --- a/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java +++ b/src/java.desktop/share/classes/sun/awt/image/SurfaceManager.java @@ -89,7 +89,7 @@ public static void setManager(Image img, SurfaceManager mgr) { imgaccessor.setSurfaceManager(img, mgr); } - private ConcurrentHashMap cacheMap; + private volatile ConcurrentHashMap cacheMap; /** * Return an arbitrary cached object for an arbitrary cache key. diff --git a/src/java.desktop/share/classes/sun/font/FontUtilities.java b/src/java.desktop/share/classes/sun/font/FontUtilities.java index 79ea7de393c..550cdcce3cf 100644 --- a/src/java.desktop/share/classes/sun/font/FontUtilities.java +++ b/src/java.desktop/share/classes/sun/font/FontUtilities.java @@ -38,7 +38,6 @@ /** * A collection of utility methods. */ -@SuppressWarnings("removal") public final class FontUtilities { public static boolean isLinux; @@ -56,7 +55,11 @@ public final class FontUtilities { // This static initializer block figures out the OS constants. static { + initStatic(); + } + @SuppressWarnings("removal") + private static void initStatic() { AccessController.doPrivileged(new PrivilegedAction() { @SuppressWarnings("deprecation") // PlatformLogger.setLevel is deprecated. @Override diff --git a/src/java.desktop/share/classes/sun/font/StrikeCache.java b/src/java.desktop/share/classes/sun/font/StrikeCache.java index c550b63562b..866498df6ee 100644 --- a/src/java.desktop/share/classes/sun/font/StrikeCache.java +++ b/src/java.desktop/share/classes/sun/font/StrikeCache.java @@ -61,7 +61,6 @@ */ -@SuppressWarnings("removal") public final class StrikeCache { static final Unsafe unsafe = Unsafe.getUnsafe(); @@ -135,6 +134,11 @@ public final class StrikeCache { static native void getGlyphCacheDescription(long[] infoArray); static { + initStatic(); + } + + @SuppressWarnings("removal") + private static void initStatic() { long[] nativeInfo = new long[13]; getGlyphCacheDescription(nativeInfo); diff --git a/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java b/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java index 014281e0811..e7ef49b1fc9 100644 --- a/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java +++ b/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java @@ -59,21 +59,19 @@ * @see GraphicsDevice * @see GraphicsConfiguration */ -@SuppressWarnings("removal") public abstract class SunGraphicsEnvironment extends GraphicsEnvironment implements DisplayChangedListener { /** Establish the default font to be used by SG2D. */ private final Font defaultFont = new Font(Font.DIALOG, Font.PLAIN, 12); - private static final boolean uiScaleEnabled; - private static final double debugScale; + @SuppressWarnings("removal") + private static final boolean uiScaleEnabled + = "true".equals(AccessController.doPrivileged( + new GetPropertyAction("sun.java2d.uiScale.enabled", "true"))); - static { - uiScaleEnabled = "true".equals(AccessController.doPrivileged( - new GetPropertyAction("sun.java2d.uiScale.enabled", "true"))); - debugScale = uiScaleEnabled ? getScaleFactor("sun.java2d.uiScale") : -1; - } + private static final double debugScale = + uiScaleEnabled ? getScaleFactor("sun.java2d.uiScale") : -1; protected GraphicsDevice[] screens; @@ -299,6 +297,7 @@ public static double getDebugScale() { public static double getScaleFactor(String propertyName) { + @SuppressWarnings("removal") String scaleFactor = AccessController.doPrivileged( new GetPropertyAction(propertyName, "-1")); diff --git a/src/java.desktop/share/classes/sun/swing/JLightweightFrame.java b/src/java.desktop/share/classes/sun/swing/JLightweightFrame.java index 609ec8c7aa9..c1167b4d8ba 100644 --- a/src/java.desktop/share/classes/sun/swing/JLightweightFrame.java +++ b/src/java.desktop/share/classes/sun/swing/JLightweightFrame.java @@ -68,7 +68,7 @@ * @author Artem Ananiev * @author Anton Tarasov */ -@SuppressWarnings({"removal","serial"}) // JDK-implementation class +@SuppressWarnings("serial") // JDK-implementation class public final class JLightweightFrame extends LightweightFrame implements RootPaneContainer { private final JRootPane rootPane = new JRootPane(); @@ -83,19 +83,6 @@ public final class JLightweightFrame extends LightweightFrame implements RootPan private volatile double scaleFactorX; private volatile double scaleFactorY; - /** - * {@code copyBufferEnabled}, true by default, defines the following strategy. - * A duplicating (copy) buffer is created for the original pixel buffer. - * The copy buffer is synchronized with the original buffer every time the - * latter changes. {@code JLightweightFrame} passes the copy buffer array - * to the {@link LightweightContent#imageBufferReset} method. The code spot - * which synchronizes two buffers becomes the only critical section guarded - * by the lock (managed with the {@link LightweightContent#paintLock()}, - * {@link LightweightContent#paintUnlock()} methods). - */ - private static boolean copyBufferEnabled; - private int[] copyBuffer; - private PropertyChangeListener layoutSizeListener; private RepaintListener repaintListener; @@ -106,14 +93,28 @@ public void updateCursor(JLightweightFrame frame) { frame.updateClientCursor(); } }); - copyBufferEnabled = "true".equals(AccessController. - doPrivileged(new GetPropertyAction("swing.jlf.copyBufferEnabled", "true"))); } + /** + * {@code copyBufferEnabled}, true by default, defines the following strategy. + * A duplicating (copy) buffer is created for the original pixel buffer. + * The copy buffer is synchronized with the original buffer every time the + * latter changes. {@code JLightweightFrame} passes the copy buffer array + * to the {@link LightweightContent#imageBufferReset} method. The code spot + * which synchronizes two buffers becomes the only critical section guarded + * by the lock (managed with the {@link LightweightContent#paintLock()}, + * {@link LightweightContent#paintUnlock()} methods). + */ + @SuppressWarnings("removal") + private static boolean copyBufferEnabled = "true".equals(AccessController. + doPrivileged(new GetPropertyAction("swing.jlf.copyBufferEnabled", "true"))); + private int[] copyBuffer; + /** * Constructs a new, initially invisible {@code JLightweightFrame} * instance. */ + @SuppressWarnings("removal") public JLightweightFrame() { super(); AffineTransform defaultTransform = @@ -330,7 +331,7 @@ private void notifyImageUpdated(int x, int y, int width, int height) { content.imageUpdated(x, y, width, height); } - @SuppressWarnings("serial") // anonymous class inside + @SuppressWarnings({"removal","serial"}) // anonymous class inside private void initInterior() { contentPane = new JPanel() { @Override diff --git a/src/java.desktop/share/legal/harfbuzz.md b/src/java.desktop/share/legal/harfbuzz.md index 9037354540b..3426352d51f 100644 --- a/src/java.desktop/share/legal/harfbuzz.md +++ b/src/java.desktop/share/legal/harfbuzz.md @@ -1,8 +1,8 @@ -## Harfbuzz v4.4.1 +## Harfbuzz v7.0.1 ### Harfbuzz License -https://github.com/harfbuzz/harfbuzz/blob/4.4.1/COPYING +https://github.com/harfbuzz/harfbuzz/blob/7.0.1/COPYING
     
    @@ -12,21 +12,22 @@ files names COPYING in subdirectories where applicable.
     
     Copyright © 2010-2022  Google, Inc.
     Copyright © 2018-2020  Ebrahim Byagowi
    -Copyright © 2019-2020  Facebook, Inc.
    -Copyright © 2012-2015  Mozilla Foundation.
    -Copyright © 2011  Codethink Limited
    -Copyright © 2008-2010  Nokia Corporation and/or its subsidiary(-ies)
    -Copyright © 2009  Keith Stribley
    -Copyright © 2009  Martin Hosken and SIL International
    -Copyright © 2007  Chris Wilson
    -Copyright © 2005-2022 Behdad Esfahbod
    -Copyright © 2005  David Turner
     Copyright © 2004-2013  Red Hat, Inc.
    -Copyright © 1998-2004  David Turner and Werner Lemberg
    -Copyright © 2016  Elie Roux 
    +Copyright © 2019  Facebook, Inc.
    +Copyright © 2007  Chris Wilson
     Copyright © 2018-2019 Adobe Inc.
    +Copyright © 2006-2023 Behdad Esfahbod
    +Copyright © 1998-2004  David Turner and Werner Lemberg
    +Copyright © 2009  Keith Stribley
     Copyright © 2018  Khaled Hosny
    +Copyright © 2016  Elie Roux 
     Copyright © 2016  Igalia S.L.
    +Copyright © 2015  Mozilla Foundation.
    +Copyright © 1999  David Turner
    +Copyright © 2005  Werner Lemberg
    +Copyright © 2013-2015  Alexei Podtelezhnikov
    +Copyright © 2022 Matthias Clasen
    +Copyright © 2011  Codethink Limited
     
     For full copyright notices consult the individual files in the package.
     
    @@ -72,3 +73,23 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     
     
    + +### AUTHORS File Information +``` + +Behdad Esfahbod +David Corbett +David Turner +Ebrahim Byagowi +Garret Rieger +Jonathan Kew +Khaled Hosny +Lars Knoll +Martin Hosken +Owen Taylor +Roderick Sheeter +Roozbeh Pournader +Simon Hausmann +Werner Lemberg + +``` diff --git a/src/java.desktop/share/legal/lcms.md b/src/java.desktop/share/legal/lcms.md index 1576edb8db6..da86a9c47ca 100644 --- a/src/java.desktop/share/legal/lcms.md +++ b/src/java.desktop/share/legal/lcms.md @@ -1,8 +1,7 @@ -## Little Color Management System (LCMS) v2.14 +## Little Color Management System (LCMS) v2.15 ### LCMS License
    -
     README.1ST file information
     
     LittleCMS core is released under MIT License
    @@ -10,7 +9,7 @@ LittleCMS core is released under MIT License
     ---------------------------------
     
     Little CMS
    -Copyright (c) 1998-2022 Marti Maria Saguer
    +Copyright (c) 1998-2023 Marti Maria Saguer
     
     Permission is hereby granted, free of charge, to any person obtaining
     a copy of this software and associated documentation files (the
    @@ -32,7 +31,6 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     
     ---------------------------------
    -
     The below license applies to the following files:
     liblcms/cmssm.c
     
    @@ -44,12 +42,12 @@ SoftSurfer makes no warranty for this code, and cannot be held
     liable for any real or imagined damage resulting from its use.
     Users of this code must verify correctness for their application.
     
    -
     
    ### AUTHORS File Information ``` + Main Author ------------ Marti Maria @@ -90,11 +88,15 @@ Mark Allen Noel Carboni Sergei Trofimovic Philipp Knechtges +Amyspark +Lovell Fuller +Eli Schwartz Special Thanks -------------- Artifex software AlienSkin software +libVIPS Jan Morovic Jos Vernon (WebSupergoo) Harald Schneider (Maxon) @@ -103,5 +105,4 @@ Dimitrios Anastassakis Lemke Software Tim Zaman - ``` diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cbdt-table.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/CBDT/CBDT.hh similarity index 93% rename from src/java.desktop/share/native/libharfbuzz/hb-ot-color-cbdt-table.hh rename to src/java.desktop/share/native/libharfbuzz/OT/Color/CBDT/CBDT.hh index 70d78c74d3b..11aeda5297f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cbdt-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/CBDT/CBDT.hh @@ -24,10 +24,11 @@ * Google Author(s): Seigo Nonaka, Calder Kitagawa */ -#ifndef HB_OT_COLOR_CBDT_TABLE_HH -#define HB_OT_COLOR_CBDT_TABLE_HH +#ifndef OT_COLOR_CBDT_CBDT_HH +#define OT_COLOR_CBDT_CBDT_HH -#include "hb-open-type.hh" +#include "../../../hb-open-type.hh" +#include "../../../hb-paint.hh" /* * CBLC -- Color Bitmap Location @@ -67,7 +68,7 @@ _copy_data_to_cbdt (hb_vector_t *cbdt_prime, { unsigned int new_len = cbdt_prime->length + length; if (unlikely (!cbdt_prime->alloc (new_len))) return false; - memcpy (cbdt_prime->arrayZ + cbdt_prime->length, data, length); + hb_memcpy (cbdt_prime->arrayZ + cbdt_prime->length, data, length); cbdt_prime->length = new_len; return true; } @@ -80,12 +81,15 @@ struct SmallGlyphMetrics return_trace (c->check_struct (this)); } - void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) const + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scale) const { - extents->x_bearing = font->em_scale_x (bearingX); - extents->y_bearing = font->em_scale_y (bearingY); - extents->width = font->em_scale_x (width); - extents->height = font->em_scale_y (-static_cast(height)); + extents->x_bearing = bearingX; + extents->y_bearing = bearingY; + extents->width = width; + extents->height = -static_cast (height); + + if (scale) + font->scale_glyph_extents (extents); } HBUINT8 height; @@ -307,7 +311,7 @@ struct IndexSubtable } } - bool get_extents (hb_glyph_extents_t *extents HB_UNUSED) const + bool get_extents (hb_glyph_extents_t *extents HB_UNUSED, bool scale HB_UNUSED) const { switch (u.header.indexFormat) { @@ -468,13 +472,13 @@ struct IndexSubtableRecord if (unlikely (!c->serializer->check_success (records->resize (records->length + 1)))) return_trace (false); - (*records)[records->length - 1].firstGlyphIndex = 1; - (*records)[records->length - 1].lastGlyphIndex = 0; + records->tail ().firstGlyphIndex = 1; + records->tail ().lastGlyphIndex = 0; bitmap_size_context->size += IndexSubtableRecord::min_size; c->serializer->push (); - if (unlikely (!add_new_subtable (c, bitmap_size_context, &((*records)[records->length - 1]), lookup, base, start))) + if (unlikely (!add_new_subtable (c, bitmap_size_context, &(records->tail ()), lookup, base, start))) { c->serializer->pop_discard (); c->serializer->revert (snap); @@ -504,8 +508,8 @@ struct IndexSubtableRecord return num_missing; } - bool get_extents (hb_glyph_extents_t *extents, const void *base) const - { return (base+offsetToSubtable).get_extents (extents); } + bool get_extents (hb_glyph_extents_t *extents, const void *base, bool scale) const + { return (base+offsetToSubtable).get_extents (extents, scale); } bool get_image_data (unsigned int gid, const void *base, @@ -833,7 +837,7 @@ struct CBDT } bool - get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const + get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents, bool scale = true) const { const void *base; const BitmapSizeTable &strike = this->cblc->choose_strike (font); @@ -841,7 +845,7 @@ struct CBDT if (!subtable_record || !strike.ppemX || !strike.ppemY) return false; - if (subtable_record->get_extents (extents, base)) + if (subtable_record->get_extents (extents, base, scale)) return true; unsigned int image_offset = 0, image_length = 0, image_format = 0; @@ -858,26 +862,29 @@ struct CBDT if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) return false; auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset); - glyphFormat17.glyphMetrics.get_extents (font, extents); + glyphFormat17.glyphMetrics.get_extents (font, extents, scale); break; } case 18: { if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) return false; auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset); - glyphFormat18.glyphMetrics.get_extents (font, extents); + glyphFormat18.glyphMetrics.get_extents (font, extents, scale); break; } default: return false; /* TODO: Support other image formats. */ } /* Convert to font units. */ - float x_scale = upem / (float) strike.ppemX; - float y_scale = upem / (float) strike.ppemY; - extents->x_bearing = roundf (extents->x_bearing * x_scale); - extents->y_bearing = roundf (extents->y_bearing * y_scale); - extents->width = roundf (extents->width * x_scale); - extents->height = roundf (extents->height * y_scale); + if (scale) + { + float x_scale = upem / (float) strike.ppemX; + float y_scale = upem / (float) strike.ppemY; + extents->x_bearing = roundf (extents->x_bearing * x_scale); + extents->y_bearing = roundf (extents->y_bearing * y_scale); + extents->width = roundf (extents->width * x_scale); + extents->height = roundf (extents->height * y_scale); + } return true; } @@ -934,6 +941,32 @@ struct CBDT bool has_data () const { return cbdt.get_length (); } + bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const + { + hb_glyph_extents_t extents; + hb_glyph_extents_t pixel_extents; + hb_blob_t *blob = reference_png (font, glyph); + + if (unlikely (blob == hb_blob_get_empty ())) + return false; + + if (unlikely (!hb_font_get_glyph_extents (font, glyph, &extents))) + return false; + + if (unlikely (!get_extents (font, glyph, &pixel_extents, false))) + return false; + + bool ret = funcs->image (data, + blob, + pixel_extents.width, -pixel_extents.height, + HB_PAINT_IMAGE_FORMAT_PNG, + font->slant_xy, + &extents); + + hb_blob_destroy (blob); + return ret; + } + private: hb_blob_ptr_t cblc; hb_blob_ptr_t cbdt; @@ -994,4 +1027,4 @@ struct CBDT_accelerator_t : CBDT::accelerator_t { } /* namespace OT */ -#endif /* HB_OT_COLOR_CBDT_TABLE_HH */ +#endif /* OT_COLOR_CBDT_CBDT_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colr-table.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh similarity index 68% rename from src/java.desktop/share/native/libharfbuzz/hb-ot-color-colr-table.hh rename to src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh index 6b9e734615b..c286ee3c908 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colr-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh @@ -25,12 +25,14 @@ * Google Author(s): Calder Kitagawa */ -#ifndef HB_OT_COLOR_COLR_TABLE_HH -#define HB_OT_COLOR_COLR_TABLE_HH +#ifndef OT_COLOR_COLR_COLR_HH +#define OT_COLOR_COLR_COLR_HH -#include "hb-open-type.hh" -#include "hb-ot-layout-common.hh" -#include "hb-ot-var-common.hh" +#include "../../../hb.hh" +#include "../../../hb-open-type.hh" +#include "../../../hb-ot-var-common.hh" +#include "../../../hb-paint.hh" +#include "../../../hb-paint-extents.hh" /* * COLR -- Color @@ -38,17 +40,82 @@ */ #define HB_OT_TAG_COLR HB_TAG('C','O','L','R') -#ifndef HB_COLRV1_MAX_NESTING_LEVEL -#define HB_COLRV1_MAX_NESTING_LEVEL 100 -#endif -#ifndef COLRV1_ENABLE_SUBSETTING -#define COLRV1_ENABLE_SUBSETTING 1 -#endif +namespace OT { +struct hb_paint_context_t; +} namespace OT { struct COLR; + +struct Paint; + +struct hb_paint_context_t : + hb_dispatch_context_t +{ + template + return_t dispatch (const T &obj) { obj.paint_glyph (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } + + const COLR* get_colr_table () const + { return reinterpret_cast (base); } + +public: + const void *base; + hb_paint_funcs_t *funcs; + void *data; + hb_font_t *font; + unsigned int palette_index; + hb_color_t foreground; + VarStoreInstancer &instancer; + int depth_left = HB_MAX_NESTING_LEVEL; + int edge_count = HB_COLRV1_MAX_EDGE_COUNT; + + hb_paint_context_t (const void *base_, + hb_paint_funcs_t *funcs_, + void *data_, + hb_font_t *font_, + unsigned int palette_, + hb_color_t foreground_, + VarStoreInstancer &instancer_) : + base (base_), + funcs (funcs_), + data (data_), + font (font_), + palette_index (palette_), + foreground (foreground_), + instancer (instancer_) + { } + + hb_color_t get_color (unsigned int color_index, float alpha, hb_bool_t *is_foreground) + { + hb_color_t color = foreground; + + *is_foreground = true; + + if (color_index != 0xffff) + { + if (!funcs->custom_palette_color (data, color_index, &color)) + { + unsigned int clen = 1; + hb_face_t *face = hb_font_get_face (font); + + hb_ot_color_palette_get_colors (face, palette_index, color_index, &clen, &color); + } + + *is_foreground = false; + } + + return HB_COLOR (hb_color_get_blue (color), + hb_color_get_green (color), + hb_color_get_red (color), + hb_color_get_alpha (color) * alpha); + } + + inline void recurse (const Paint &paint); +}; + struct hb_colrv1_closure_context_t : hb_dispatch_context_t { @@ -102,7 +169,7 @@ struct hb_colrv1_closure_context_t : hb_set_t *glyphs_, hb_set_t *layer_indices_, hb_set_t *palette_indices_, - unsigned nesting_level_left_ = HB_COLRV1_MAX_NESTING_LEVEL) : + unsigned nesting_level_left_ = HB_MAX_NESTING_LEVEL) : base (base_), glyphs (glyphs_), layer_indices (layer_indices_), @@ -145,7 +212,7 @@ struct BaseGlyphRecord bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); + return_trace (c->check_struct (this)); } public: @@ -164,6 +231,8 @@ struct BaseGlyphRecord template struct Variable { + static constexpr bool is_variable = true; + Variable* copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); @@ -186,8 +255,26 @@ struct Variable return_trace (c->check_struct (this) && value.sanitize (c)); } + void paint_glyph (hb_paint_context_t *c) const + { + value.paint_glyph (c, varIdxBase); + } + + void get_color_stop (hb_paint_context_t *c, + hb_color_stop_t *stop, + const VarStoreInstancer &instancer) const + { + value.get_color_stop (c, stop, varIdxBase, instancer); + } + + hb_paint_extend_t get_extend () const + { + return value.get_extend (); + } + protected: T value; + public: VarIdx varIdxBase; public: DEFINE_SIZE_STATIC (4 + T::static_size); @@ -196,6 +283,10 @@ struct Variable template struct NoVariable { + static constexpr bool is_variable = false; + + static constexpr uint32_t varIdxBase = VarIdx::NO_VARIATION; + NoVariable* copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); @@ -217,6 +308,23 @@ struct NoVariable return_trace (c->check_struct (this) && value.sanitize (c)); } + void paint_glyph (hb_paint_context_t *c) const + { + value.paint_glyph (c, varIdxBase); + } + + void get_color_stop (hb_paint_context_t *c, + hb_color_stop_t *stop, + const VarStoreInstancer &instancer) const + { + value.get_color_stop (c, stop, VarIdx::NO_VARIATION, instancer); + } + + hb_paint_extend_t get_extend () const + { + return value.get_extend (); + } + T value; public: DEFINE_SIZE_STATIC (T::static_size); @@ -234,7 +342,7 @@ struct ColorStop TRACE_SUBSET (this); auto *out = c->serializer->embed (*this); if (unlikely (!out)) return_trace (false); - return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes->get (paletteIndex), + return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex), HB_SERIALIZE_ERROR_INT_OVERFLOW)); } @@ -244,6 +352,17 @@ struct ColorStop return_trace (c->check_struct (this)); } + void get_color_stop (hb_paint_context_t *c, + hb_color_stop_t *out, + uint32_t varIdx, + const VarStoreInstancer &instancer) const + { + out->offset = stopOffset.to_float(instancer (varIdx, 0)); + out->color = c->get_color (paletteIndex, + alpha.to_float (instancer (varIdx, 1)), + &out->is_foreground); + } + F2DOT14 stopOffset; HBUINT16 paletteIndex; F2DOT14 alpha; @@ -295,6 +414,52 @@ struct ColorLine stops.sanitize (c)); } + /* get up to count stops from start */ + unsigned int + get_color_stops (hb_paint_context_t *c, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops, + const VarStoreInstancer &instancer) const + { + unsigned int len = stops.len; + + if (count && color_stops) + { + unsigned int i; + for (i = 0; i < *count && start + i < len; i++) + stops[start + i].get_color_stop (c, &color_stops[i], instancer); + *count = i; + } + + return len; + } + + HB_INTERNAL static unsigned int static_get_color_stops (hb_color_line_t *color_line, + void *color_line_data, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops, + void *user_data) + { + const ColorLine *thiz = (const ColorLine *) color_line_data; + hb_paint_context_t *c = (hb_paint_context_t *) user_data; + return thiz->get_color_stops (c, start, count, color_stops, c->instancer); + } + + hb_paint_extend_t get_extend () const + { + return (hb_paint_extend_t) (unsigned int) extend; + } + + HB_INTERNAL static hb_paint_extend_t static_get_extend (hb_color_line_t *color_line, + void *color_line_data, + void *user_data) + { + const ColorLine *thiz = (const ColorLine *) color_line_data; + return thiz->get_extend (); + } + Extend extend; Array16Of> stops; public: @@ -358,14 +523,25 @@ struct Affine2x3 return_trace (c->check_struct (this)); } - HBFixed xx; - HBFixed yx; - HBFixed xy; - HBFixed yy; - HBFixed dx; - HBFixed dy; + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + c->funcs->push_transform (c->data, + xx.to_float (c->instancer (varIdxBase, 0)), + yx.to_float (c->instancer (varIdxBase, 1)), + xy.to_float (c->instancer (varIdxBase, 2)), + yy.to_float (c->instancer (varIdxBase, 3)), + dx.to_float (c->instancer (varIdxBase, 4)), + dy.to_float (c->instancer (varIdxBase, 5))); + } + + F16DOT16 xx; + F16DOT16 yx; + F16DOT16 xy; + F16DOT16 yy; + F16DOT16 dx; + F16DOT16 dy; public: - DEFINE_SIZE_STATIC (6 * HBFixed::static_size); + DEFINE_SIZE_STATIC (6 * F16DOT16::static_size); }; struct PaintColrLayers @@ -377,7 +553,7 @@ struct PaintColrLayers TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); - return_trace (c->serializer->check_assign (out->firstLayerIndex, c->plan->colrv1_layers->get (firstLayerIndex), + return_trace (c->serializer->check_assign (out->firstLayerIndex, c->plan->colrv1_layers.get (firstLayerIndex), HB_SERIALIZE_ERROR_INT_OVERFLOW)); return_trace (true); @@ -389,6 +565,8 @@ struct PaintColrLayers return_trace (c->check_struct (this)); } + inline void paint_glyph (hb_paint_context_t *c) const; + HBUINT8 format; /* format = 1 */ HBUINT8 numLayers; HBUINT32 firstLayerIndex; /* index into COLRv1::layerList */ @@ -406,7 +584,7 @@ struct PaintSolid TRACE_SUBSET (this); auto *out = c->serializer->embed (*this); if (unlikely (!out)) return_trace (false); - return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes->get (paletteIndex), + return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes.get (paletteIndex), HB_SERIALIZE_ERROR_INT_OVERFLOW)); } @@ -416,6 +594,17 @@ struct PaintSolid return_trace (c->check_struct (this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + hb_bool_t is_foreground; + hb_color_t color; + + color = c->get_color (paletteIndex, + alpha.to_float (c->instancer (varIdxBase, 0)), + &is_foreground); + c->funcs->color (c->data, is_foreground, color); + } + HBUINT8 format; /* format = 2(noVar) or 3(Var)*/ HBUINT16 paletteIndex; F2DOT14 alpha; @@ -444,6 +633,23 @@ struct PaintLinearGradient return_trace (c->check_struct (this) && colorLine.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + hb_color_line_t cl = { + (void *) &(this+colorLine), + (this+colorLine).static_get_color_stops, c, + (this+colorLine).static_get_extend, nullptr + }; + + c->funcs->linear_gradient (c->data, &cl, + x0 + c->instancer (varIdxBase, 0), + y0 + c->instancer (varIdxBase, 1), + x1 + c->instancer (varIdxBase, 2), + y1 + c->instancer (varIdxBase, 3), + x2 + c->instancer (varIdxBase, 4), + y2 + c->instancer (varIdxBase, 5)); + } + HBUINT8 format; /* format = 4(noVar) or 5 (Var) */ Offset24To> colorLine; /* Offset (from beginning of PaintLinearGradient * table) to ColorLine subtable. */ @@ -478,6 +684,23 @@ struct PaintRadialGradient return_trace (c->check_struct (this) && colorLine.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + hb_color_line_t cl = { + (void *) &(this+colorLine), + (this+colorLine).static_get_color_stops, c, + (this+colorLine).static_get_extend, nullptr + }; + + c->funcs->radial_gradient (c->data, &cl, + x0 + c->instancer (varIdxBase, 0), + y0 + c->instancer (varIdxBase, 1), + radius0 + c->instancer (varIdxBase, 2), + x1 + c->instancer (varIdxBase, 3), + y1 + c->instancer (varIdxBase, 4), + radius1 + c->instancer (varIdxBase, 5)); + } + HBUINT8 format; /* format = 6(noVar) or 7 (Var) */ Offset24To> colorLine; /* Offset (from beginning of PaintRadialGradient * table) to ColorLine subtable. */ @@ -512,6 +735,21 @@ struct PaintSweepGradient return_trace (c->check_struct (this) && colorLine.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + hb_color_line_t cl = { + (void *) &(this+colorLine), + (this+colorLine).static_get_color_stops, c, + (this+colorLine).static_get_extend, nullptr + }; + + c->funcs->sweep_gradient (c->data, &cl, + centerX + c->instancer (varIdxBase, 0), + centerY + c->instancer (varIdxBase, 1), + (startAngle.to_float (c->instancer (varIdxBase, 2)) + 1) * (float) M_PI, + (endAngle.to_float (c->instancer (varIdxBase, 3)) + 1) * (float) M_PI); + } + HBUINT8 format; /* format = 8(noVar) or 9 (Var) */ Offset24To> colorLine; /* Offset (from beginning of PaintSweepGradient * table) to ColorLine subtable. */ @@ -523,7 +761,6 @@ struct PaintSweepGradient DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size + 2 * F2DOT14::static_size); }; -struct Paint; // Paint a non-COLR glyph, filled as indicated by paint. struct PaintGlyph { @@ -548,6 +785,17 @@ struct PaintGlyph return_trace (c->check_struct (this) && paint.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c) const + { + c->funcs->push_inverse_root_transform (c->data, c->font); + c->funcs->push_clip_glyph (c->data, gid, c->font); + c->funcs->push_root_transform (c->data, c->font); + c->recurse (this+paint); + c->funcs->pop_transform (c->data); + c->funcs->pop_clip (c->data); + c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 10 */ Offset24To paint; /* Offset (from beginning of PaintGlyph table) to Paint subtable. */ HBUINT16 gid; @@ -575,6 +823,8 @@ struct PaintColrGlyph return_trace (c->check_struct (this)); } + inline void paint_glyph (hb_paint_context_t *c) const; + HBUINT8 format; /* format = 11 */ HBUINT16 gid; public: @@ -603,6 +853,13 @@ struct PaintTransform transform.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c) const + { + (this+transform).paint_glyph (c); + c->recurse (this+src); + c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 12(noVar) or 13 (Var) */ Offset24To src; /* Offset (from beginning of PaintTransform table) to Paint subtable. */ Offset24To> transform; @@ -629,6 +886,16 @@ struct PaintTranslate return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float ddx = dx + c->instancer (varIdxBase, 0); + float ddy = dy + c->instancer (varIdxBase, 1); + + bool p1 = c->funcs->push_translate (c->data, ddx, ddy); + c->recurse (this+src); + if (p1) c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 14(noVar) or 15 (Var) */ Offset24To src; /* Offset (from beginning of PaintTranslate table) to Paint subtable. */ FWORD dx; @@ -656,6 +923,16 @@ struct PaintScale return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float sx = scaleX.to_float (c->instancer (varIdxBase, 0)); + float sy = scaleY.to_float (c->instancer (varIdxBase, 1)); + + bool p1 = c->funcs->push_scale (c->data, sx, sy); + c->recurse (this+src); + if (p1) c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 16 (noVar) or 17(Var) */ Offset24To src; /* Offset (from beginning of PaintScale table) to Paint subtable. */ F2DOT14 scaleX; @@ -683,6 +960,22 @@ struct PaintScaleAroundCenter return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float sx = scaleX.to_float (c->instancer (varIdxBase, 0)); + float sy = scaleY.to_float (c->instancer (varIdxBase, 1)); + float tCenterX = centerX + c->instancer (varIdxBase, 2); + float tCenterY = centerY + c->instancer (varIdxBase, 3); + + bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); + bool p2 = c->funcs->push_scale (c->data, sx, sy); + bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->recurse (this+src); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 18 (noVar) or 19(Var) */ Offset24To src; /* Offset (from beginning of PaintScaleAroundCenter table) to Paint subtable. */ F2DOT14 scaleX; @@ -712,6 +1005,15 @@ struct PaintScaleUniform return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float s = scale.to_float (c->instancer (varIdxBase, 0)); + + bool p1 = c->funcs->push_scale (c->data, s, s); + c->recurse (this+src); + if (p1) c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 20 (noVar) or 21(Var) */ Offset24To src; /* Offset (from beginning of PaintScaleUniform table) to Paint subtable. */ F2DOT14 scale; @@ -738,6 +1040,21 @@ struct PaintScaleUniformAroundCenter return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float s = scale.to_float (c->instancer (varIdxBase, 0)); + float tCenterX = centerX + c->instancer (varIdxBase, 1); + float tCenterY = centerY + c->instancer (varIdxBase, 2); + + bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); + bool p2 = c->funcs->push_scale (c->data, s, s); + bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->recurse (this+src); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 22 (noVar) or 23(Var) */ Offset24To src; /* Offset (from beginning of PaintScaleUniformAroundCenter table) to Paint subtable. */ F2DOT14 scale; @@ -766,6 +1083,15 @@ struct PaintRotate return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float a = angle.to_float (c->instancer (varIdxBase, 0)); + + bool p1 = c->funcs->push_rotate (c->data, a); + c->recurse (this+src); + if (p1) c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 24 (noVar) or 25(Var) */ Offset24To src; /* Offset (from beginning of PaintRotate table) to Paint subtable. */ F2DOT14 angle; @@ -792,6 +1118,21 @@ struct PaintRotateAroundCenter return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float a = angle.to_float (c->instancer (varIdxBase, 0)); + float tCenterX = centerX + c->instancer (varIdxBase, 1); + float tCenterY = centerY + c->instancer (varIdxBase, 2); + + bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); + bool p2 = c->funcs->push_rotate (c->data, a); + bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->recurse (this+src); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 26 (noVar) or 27(Var) */ Offset24To src; /* Offset (from beginning of PaintRotateAroundCenter table) to Paint subtable. */ F2DOT14 angle; @@ -820,6 +1161,16 @@ struct PaintSkew return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0)); + float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1)); + + bool p1 = c->funcs->push_skew (c->data, sx, sy); + c->recurse (this+src); + if (p1) c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 28(noVar) or 29 (Var) */ Offset24To src; /* Offset (from beginning of PaintSkew table) to Paint subtable. */ F2DOT14 xSkewAngle; @@ -847,6 +1198,22 @@ struct PaintSkewAroundCenter return_trace (c->check_struct (this) && src.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c, uint32_t varIdxBase) const + { + float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0)); + float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1)); + float tCenterX = centerX + c->instancer (varIdxBase, 2); + float tCenterY = centerY + c->instancer (varIdxBase, 3); + + bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); + bool p2 = c->funcs->push_skew (c->data, sx, sy); + bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->recurse (this+src); + if (p3) c->funcs->pop_transform (c->data); + if (p2) c->funcs->pop_transform (c->data); + if (p1) c->funcs->pop_transform (c->data); + } + HBUINT8 format; /* format = 30(noVar) or 31 (Var) */ Offset24To src; /* Offset (from beginning of PaintSkewAroundCenter table) to Paint subtable. */ F2DOT14 xSkewAngle; @@ -879,6 +1246,14 @@ struct PaintComposite backdrop.sanitize (c, this)); } + void paint_glyph (hb_paint_context_t *c) const + { + c->recurse (this+backdrop); + c->funcs->push_group (c->data); + c->recurse (this+src); + c->funcs->pop_group (c->data, (hb_paint_composite_mode_t) (int) mode); + } + HBUINT8 format; /* format = 32 */ Offset24To src; /* Offset (from beginning of PaintComposite table) to source Paint subtable. */ CompositeMode mode; /* If mode is unrecognized use COMPOSITE_CLEAR */ @@ -887,6 +1262,11 @@ struct PaintComposite DEFINE_SIZE_STATIC (8); }; +struct ClipBoxData +{ + int xMin, yMin, xMax, yMax; +}; + struct ClipBoxFormat1 { bool sanitize (hb_sanitize_context_t *c) const @@ -895,6 +1275,14 @@ struct ClipBoxFormat1 return_trace (c->check_struct (this)); } + void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer HB_UNUSED) const + { + clip_box.xMin = xMin; + clip_box.yMin = yMin; + clip_box.xMax = xMax; + clip_box.yMax = yMax; + } + public: HBUINT8 format; /* format = 1(noVar) or 2(Var)*/ FWORD xMin; @@ -905,7 +1293,20 @@ struct ClipBoxFormat1 DEFINE_SIZE_STATIC (1 + 4 * FWORD::static_size); }; -struct ClipBoxFormat2 : Variable {}; +struct ClipBoxFormat2 : Variable +{ + void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer) const + { + value.get_clip_box(clip_box, instancer); + if (instancer) + { + clip_box.xMin += _hb_roundf (instancer (varIdxBase, 0)); + clip_box.yMin += _hb_roundf (instancer (varIdxBase, 1)); + clip_box.xMax += _hb_roundf (instancer (varIdxBase, 2)); + clip_box.yMax += _hb_roundf (instancer (varIdxBase, 3)); + } + } +}; struct ClipBox { @@ -922,8 +1323,8 @@ struct ClipBox template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); - if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); @@ -931,6 +1332,28 @@ struct ClipBox } } + bool get_extents (hb_glyph_extents_t *extents, + const VarStoreInstancer &instancer) const + { + ClipBoxData clip_box; + switch (u.format) { + case 1: + u.format1.get_clip_box (clip_box, instancer); + break; + case 2: + u.format2.get_clip_box (clip_box, instancer); + break; + default: + return false; + } + + extents->x_bearing = clip_box.xMin; + extents->y_bearing = clip_box.yMax; + extents->width = clip_box.xMax - clip_box.xMin; + extents->height = clip_box.yMin - clip_box.yMax; + return true; + } + protected: union { HBUINT8 format; /* Format identifier */ @@ -941,6 +1364,9 @@ struct ClipBox struct ClipRecord { + int cmp (hb_codepoint_t g) const + { return g < startGlyphID ? -1 : g <= endGlyphID ? 0 : +1; } + ClipRecord* copy (hb_serialize_context_t *c, const void *base) const { TRACE_SERIALIZE (this); @@ -956,6 +1382,13 @@ struct ClipRecord return_trace (c->check_struct (this) && clipBox.sanitize (c, base)); } + bool get_extents (hb_glyph_extents_t *extents, + const void *base, + const VarStoreInstancer &instancer) const + { + return (base+clipBox).get_extents (extents, instancer); + } + public: HBUINT16 startGlyphID; // first gid clip applies to HBUINT16 endGlyphID; // last gid clip applies to, inclusive @@ -963,6 +1396,7 @@ struct ClipRecord public: DEFINE_SIZE_STATIC (7); }; +DECLARE_NULL_NAMESPACE_BYTES (OT, ClipRecord); struct ClipList { @@ -1025,7 +1459,7 @@ struct ClipList if (unlikely (!c->serializer->extend_min (out))) return_trace (false); if (!c->serializer->check_assign (out->format, format, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); - const hb_set_t& glyphset = *c->plan->_glyphset_colred; + const hb_set_t& glyphset = c->plan->_glyphset_colred; const hb_map_t &glyph_map = *c->plan->glyph_map; hb_map_t new_gid_offset_map; @@ -1051,11 +1485,26 @@ struct ClipList bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); + // TODO Make a formatted struct! return_trace (c->check_struct (this) && clips.sanitize (c, this)); } + bool + get_extents (hb_codepoint_t gid, + hb_glyph_extents_t *extents, + const VarStoreInstancer &instancer) const + { + auto *rec = clips.as_array ().bsearch (gid); + if (rec) + { + rec->get_extents (extents, this, instancer); + return true; + } + return false; + } + HBUINT8 format; // Set to 1. - Array32Of clips; // Clip records, sorted by startGlyphID + SortedArray32Of clips; // Clip records, sorted by startGlyphID public: DEFINE_SIZE_ARRAY_SIZED (5, clips); }; @@ -1068,7 +1517,7 @@ struct Paint { TRACE_SANITIZE (this); - if (unlikely (!c->check_start_recursion (HB_COLRV1_MAX_NESTING_LEVEL))) + if (unlikely (!c->check_start_recursion (HB_MAX_NESTING_LEVEL))) return_trace (c->no_dispatch_return_value ()); return_trace (c->end_recursion (this->dispatch (c, std::forward (ds)...))); @@ -1077,8 +1526,8 @@ struct Paint template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); - if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.paintformat1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.paintformat2, std::forward (ds)...)); @@ -1120,38 +1569,40 @@ struct Paint union { HBUINT8 format; PaintColrLayers paintformat1; - PaintSolid paintformat2; + NoVariable paintformat2; Variable paintformat3; - PaintLinearGradient paintformat4; + NoVariable> paintformat4; Variable> paintformat5; - PaintRadialGradient paintformat6; + NoVariable> paintformat6; Variable> paintformat7; - PaintSweepGradient paintformat8; + NoVariable> paintformat8; Variable> paintformat9; PaintGlyph paintformat10; PaintColrGlyph paintformat11; PaintTransform paintformat12; PaintTransform paintformat13; - PaintTranslate paintformat14; + NoVariable paintformat14; Variable paintformat15; - PaintScale paintformat16; + NoVariable paintformat16; Variable paintformat17; - PaintScaleAroundCenter paintformat18; + NoVariable paintformat18; Variable paintformat19; - PaintScaleUniform paintformat20; + NoVariable paintformat20; Variable paintformat21; - PaintScaleUniformAroundCenter paintformat22; + NoVariable paintformat22; Variable paintformat23; - PaintRotate paintformat24; + NoVariable paintformat24; Variable paintformat25; - PaintRotateAroundCenter paintformat26; + NoVariable paintformat26; Variable paintformat27; - PaintSkew paintformat28; + NoVariable paintformat28; Variable paintformat29; - PaintSkewAroundCenter paintformat30; + NoVariable paintformat30; Variable paintformat31; PaintComposite paintformat32; } u; + public: + DEFINE_SIZE_MIN (2); }; struct BaseGlyphPaintRecord @@ -1193,7 +1644,7 @@ struct BaseGlyphList : SortedArray32Of TRACE_SUBSET (this); auto *out = c->serializer->start_embed (this); if (unlikely (!c->serializer->extend_min (out))) return_trace (false); - const hb_set_t* glyphset = c->plan->_glyphset_colred; + const hb_set_t* glyphset = &c->plan->_glyphset_colred; for (const auto& _ : as_array ()) { @@ -1247,7 +1698,14 @@ struct COLR { static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR; - bool has_data () const { return numBaseGlyphs; } + bool has_v0_data () const { return numBaseGlyphs; } + bool has_v1_data () const + { + if (version == 1) + return (this+baseGlyphList).len > 0; + + return false; + } unsigned int get_glyph_layers (hb_codepoint_t glyph, unsigned int start_offset, @@ -1356,7 +1814,7 @@ struct COLR (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) && (this+layersZ).sanitize (c, numLayers) && (version == 0 || - (COLRV1_ENABLE_SUBSETTING && version == 1 && + (version == 1 && baseGlyphList.sanitize (c, this) && layerList.sanitize (c, this) && clipList.sanitize (c, this) && @@ -1427,7 +1885,7 @@ struct COLR TRACE_SUBSET (this); const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map; - const hb_set_t& glyphset = *c->plan->_glyphset_colred; + const hb_set_t& glyphset = c->plan->_glyphset_colred; auto base_it = + hb_range (c->plan->num_output_glyphs ()) @@ -1476,7 +1934,7 @@ struct COLR if (unlikely (!c->plan->new_gid_for_old_gid (out_layers[i].glyphId, &new_gid))) return hb_pair_t> (false, out_layers); out_layers[i].glyphId = new_gid; - out_layers[i].colorIdx = c->plan->colr_palettes->get (layers[i].colorIdx); + out_layers[i].colorIdx = c->plan->colr_palettes.get (layers[i].colorIdx); } return hb_pair_t> (true, out_layers); @@ -1509,10 +1967,171 @@ struct COLR colr_prime->layerList.serialize_subset (c, layerList, this); colr_prime->clipList.serialize_subset (c, clipList, this); colr_prime->varIdxMap.serialize_copy (c->serializer, varIdxMap, this); - //TODO: subset varStore once it's implemented in fonttools + colr_prime->varStore.serialize_copy (c->serializer, varStore, this); return_trace (true); } + const Paint *get_base_glyph_paint (hb_codepoint_t glyph) const + { + const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList; + const BaseGlyphPaintRecord* record = get_base_glyph_paintrecord (glyph); + if (record) + { + const Paint &paint = &baseglyph_paintrecords+record->paint; + return &paint; + } + else + return nullptr; + } + + bool + get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const + { + if (version != 1) + return false; + + VarStoreInstancer instancer (this+varStore, + this+varIdxMap, + hb_array (font->coords, font->num_coords)); + + if (get_clip (glyph, extents, instancer)) + { + font->scale_glyph_extents (extents); + return true; + } + + auto *extents_funcs = hb_paint_extents_get_funcs (); + hb_paint_extents_context_t extents_data; + bool ret = paint_glyph (font, glyph, extents_funcs, &extents_data, 0, HB_COLOR(0,0,0,0)); + + hb_extents_t e = extents_data.get_extents (); + if (e.is_void ()) + { + extents->x_bearing = 0; + extents->y_bearing = 0; + extents->width = 0; + extents->height = 0; + } + else + { + extents->x_bearing = e.xmin; + extents->y_bearing = e.ymax; + extents->width = e.xmax - e.xmin; + extents->height = e.ymin - e.ymax; + } + + return ret; + } + + bool + has_paint_for_glyph (hb_codepoint_t glyph) const + { + if (version == 1) + { + const Paint *paint = get_base_glyph_paint (glyph); + + return paint != nullptr; + } + + return false; + } + + bool get_clip (hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + const VarStoreInstancer instancer) const + { + return (this+clipList).get_extents (glyph, + extents, + instancer); + } + + bool + paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const + { + VarStoreInstancer instancer (this+varStore, + this+varIdxMap, + hb_array (font->coords, font->num_coords)); + hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer); + + if (version == 1) + { + const Paint *paint = get_base_glyph_paint (glyph); + if (paint) + { + // COLRv1 glyph + + VarStoreInstancer instancer (this+varStore, + this+varIdxMap, + hb_array (font->coords, font->num_coords)); + + bool is_bounded = true; + if (clip) + { + hb_glyph_extents_t extents; + if (get_clip (glyph, &extents, instancer)) + { + font->scale_glyph_extents (&extents); + c.funcs->push_clip_rectangle (c.data, + extents.x_bearing, + extents.y_bearing + extents.height, + extents.x_bearing + extents.width, + extents.y_bearing); + } + else + { + auto *extents_funcs = hb_paint_extents_get_funcs (); + hb_paint_extents_context_t extents_data; + + paint_glyph (font, glyph, + extents_funcs, &extents_data, + palette_index, foreground, + false); + + hb_extents_t extents = extents_data.get_extents (); + is_bounded = extents_data.is_bounded (); + + c.funcs->push_clip_rectangle (c.data, + extents.xmin, + extents.ymin, + extents.xmax, + extents.ymax); + } + } + + c.funcs->push_root_transform (c.data, font); + + if (is_bounded) + c.recurse (*paint); + + c.funcs->pop_transform (c.data); + + if (clip) + c.funcs->pop_clip (c.data); + + return true; + } + } + + const BaseGlyphRecord *record = get_base_glyph_record (glyph); + if (record && ((hb_codepoint_t) record->glyphId == glyph)) + { + // COLRv0 glyph + for (const auto &r : (this+layersZ).as_array (numLayers) + .sub_array (record->firstLayerIdx, record->numLayers)) + { + hb_bool_t is_foreground; + hb_color_t color = c.get_color (r.colorIdx, 1., &is_foreground); + c.funcs->push_clip_glyph (c.data, r.glyphId, c.font); + c.funcs->color (c.data, is_foreground, color); + c.funcs->pop_clip (c.data); + } + + return true; + } + + return false; + } + protected: HBUINT16 version; /* Table version number (starts at 0). */ HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */ @@ -1535,7 +2154,50 @@ struct COLR_accelerator_t : COLR::accelerator_t { COLR_accelerator_t (hb_face_t *face) : COLR::accelerator_t (face) {} }; -} /* namespace OT */ +void +hb_paint_context_t::recurse (const Paint &paint) +{ + if (unlikely (depth_left <= 0 || edge_count <= 0)) return; + depth_left--; + edge_count--; + paint.dispatch (this); + depth_left++; +} + +void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const +{ + const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList (); + for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++) + { + const Paint &paint = paint_offset_lists.get_paint (i); + c->funcs->push_group (c->data); + c->recurse (paint); + c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER); + } +} +void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const +{ + const COLR *colr_table = c->get_colr_table (); + const Paint *paint = colr_table->get_base_glyph_paint (gid); + + hb_glyph_extents_t extents = {0}; + bool has_clip_box = colr_table->get_clip (gid, &extents, c->instancer); + + if (has_clip_box) + c->funcs->push_clip_rectangle (c->data, + extents.x_bearing, + extents.y_bearing + extents.height, + extents.x_bearing + extents.width, + extents.y_bearing); + + if (paint) + c->recurse (*paint); + + if (has_clip_box) + c->funcs->pop_clip (c->data); +} + +} /* namespace OT */ -#endif /* HB_OT_COLOR_COLR_TABLE_HH */ +#endif /* OT_COLOR_COLR_COLR_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colrv1-closure.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/colrv1-closure.hh similarity index 94% rename from src/java.desktop/share/native/libharfbuzz/hb-ot-color-colrv1-closure.hh rename to src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/colrv1-closure.hh index fbaf2ec26b6..705863d4ade 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colrv1-closure.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/colrv1-closure.hh @@ -24,12 +24,11 @@ * */ -#ifndef HB_OT_COLR_COLRV1_CLOSURE_HH -#define HB_OT_COLR_COLRV1_CLOSURE_HH +#ifndef OT_COLOR_COLR_COLRV1_CLOSURE_HH +#define OT_COLOR_COLR_COLRV1_CLOSURE_HH -#include "hb-open-type.hh" -#include "hb-ot-layout-common.hh" -#include "hb-ot-color-colr-table.hh" +#include "../../../hb-open-type.hh" +#include "COLR.hh" /* * COLR -- Color @@ -105,4 +104,4 @@ HB_INTERNAL void PaintComposite::closurev1 (hb_colrv1_closure_context_t* c) cons } /* namespace OT */ -#endif /* HB_OT_COLR_COLRV1_CLOSURE_HH */ +#endif /* OT_COLOR_COLR_COLRV1_CLOSURE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cpal-table.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh similarity index 97% rename from src/java.desktop/share/native/libharfbuzz/hb-ot-color-cpal-table.hh rename to src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh index 5558d518190..e177190710c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cpal-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh @@ -25,12 +25,12 @@ * Google Author(s): Sascha Brawer */ -#ifndef HB_OT_COLOR_CPAL_TABLE_HH -#define HB_OT_COLOR_CPAL_TABLE_HH +#ifndef OT_COLOR_CPAL_CPAL_HH +#define OT_COLOR_CPAL_CPAL_HH -#include "hb-open-type.hh" -#include "hb-ot-color.h" -#include "hb-ot-name.h" +#include "../../../hb-open-type.hh" +#include "../../../hb-ot-color.h" +#include "../../../hb-ot-name.h" /* @@ -239,7 +239,7 @@ struct CPAL TRACE_SUBSET (this); if (!numPalettes) return_trace (false); - const hb_map_t *color_index_map = c->plan->colr_palettes; + const hb_map_t *color_index_map = &c->plan->colr_palettes; if (color_index_map->is_empty ()) return_trace (false); hb_set_t retained_color_indices; @@ -319,4 +319,4 @@ struct CPAL } /* namespace OT */ -#endif /* HB_OT_COLOR_CPAL_TABLE_HH */ +#endif /* OT_COLOR_CPAL_CPAL_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-sbix-table.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/sbix/sbix.hh similarity index 88% rename from src/java.desktop/share/native/libharfbuzz/hb-ot-color-sbix-table.hh rename to src/java.desktop/share/native/libharfbuzz/OT/Color/sbix/sbix.hh index a46b2264770..55d770e9283 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-sbix-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/sbix/sbix.hh @@ -25,11 +25,11 @@ * Google Author(s): Calder Kitagawa */ -#ifndef HB_OT_COLOR_SBIX_TABLE_HH -#define HB_OT_COLOR_SBIX_TABLE_HH +#ifndef OT_COLOR_SBIX_SBIX_HH +#define OT_COLOR_SBIX_SBIX_HH -#include "hb-open-type.hh" -#include "hb-ot-layout-common.hh" +#include "../../../hb-open-type.hh" +#include "../../../hb-paint.hh" /* * sbix -- Standard Bitmap Graphics @@ -213,10 +213,11 @@ struct sbix bool get_extents (hb_font_t *font, hb_codepoint_t glyph, - hb_glyph_extents_t *extents) const + hb_glyph_extents_t *extents, + bool scale = true) const { /* We only support PNG right now, and following function checks type. */ - return get_png_extents (font, glyph, extents); + return get_png_extents (font, glyph, extents, scale); } hb_blob_t *reference_png (hb_font_t *font, @@ -231,6 +232,37 @@ struct sbix num_glyphs, available_ppem); } + bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const + { + if (!has_data ()) + return false; + + int x_offset = 0, y_offset = 0; + unsigned int strike_ppem = 0; + hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem); + hb_glyph_extents_t extents; + hb_glyph_extents_t pixel_extents; + + if (blob == hb_blob_get_empty ()) + return false; + + if (!hb_font_get_glyph_extents (font, glyph, &extents)) + return false; + + if (unlikely (!get_extents (font, glyph, &pixel_extents, false))) + return false; + + bool ret = funcs->image (data, + blob, + pixel_extents.width, -pixel_extents.height, + HB_PAINT_IMAGE_FORMAT_PNG, + font->slant_xy, + &extents); + + hb_blob_destroy (blob); + return ret; + } + private: const SBIXStrike &choose_strike (hb_font_t *font) const @@ -285,7 +317,8 @@ struct sbix bool get_png_extents (hb_font_t *font, hb_codepoint_t glyph, - hb_glyph_extents_t *extents) const + hb_glyph_extents_t *extents, + bool scale = true) const { /* Following code is safe to call even without data. * But faster to short-circuit. */ @@ -310,22 +343,18 @@ struct sbix extents->height = -1 * png.IHDR.height; /* Convert to font units. */ - if (strike_ppem) + if (strike_ppem && scale) { float scale = font->face->get_upem () / (float) strike_ppem; - extents->x_bearing = font->em_scalef_x (extents->x_bearing * scale); - extents->y_bearing = font->em_scalef_y (extents->y_bearing * scale); - extents->width = font->em_scalef_x (extents->width * scale); - extents->height = font->em_scalef_y (extents->height * scale); - } - else - { - extents->x_bearing = font->em_scale_x (extents->x_bearing); - extents->y_bearing = font->em_scale_y (extents->y_bearing); - extents->width = font->em_scale_x (extents->width); - extents->height = font->em_scale_y (extents->height); + extents->x_bearing = roundf (extents->x_bearing * scale); + extents->y_bearing = roundf (extents->y_bearing * scale); + extents->width = roundf (extents->width * scale); + extents->height = roundf (extents->height * scale); } + if (scale) + font->scale_glyph_extents (extents); + hb_blob_destroy (blob); return strike_ppem; @@ -420,4 +449,4 @@ struct sbix_accelerator_t : sbix::accelerator_t { } /* namespace OT */ -#endif /* HB_OT_COLOR_SBIX_TABLE_HH */ +#endif /* OT_COLOR_SBIX_SBIX_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-svg-table.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/svg/svg.hh similarity index 83% rename from src/java.desktop/share/native/libharfbuzz/hb-ot-color-svg-table.hh rename to src/java.desktop/share/native/libharfbuzz/OT/Color/svg/svg.hh index 8d4dee92878..c8ff6ab743e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-svg-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/svg/svg.hh @@ -22,10 +22,12 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ -#ifndef HB_OT_COLOR_SVG_TABLE_HH -#define HB_OT_COLOR_SVG_TABLE_HH +#ifndef OT_COLOR_SVG_SVG_HH +#define OT_COLOR_SVG_SVG_HH -#include "hb-open-type.hh" +#include "../../../hb-open-type.hh" +#include "../../../hb-blob.hh" +#include "../../../hb-paint.hh" /* * SVG -- SVG (Scalable Vector Graphics) @@ -91,8 +93,31 @@ struct SVG bool has_data () const { return table->has_data (); } + bool paint_glyph (hb_font_t *font HB_UNUSED, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const + { + if (!has_data ()) + return false; + + hb_blob_t *blob = reference_blob_for_glyph (glyph); + + if (blob == hb_blob_get_empty ()) + return false; + + funcs->image (data, + blob, + 0, 0, + HB_PAINT_IMAGE_FORMAT_SVG, + font->slant_xy, + nullptr); + + hb_blob_destroy (blob); + return true; + } + private: hb_blob_ptr_t table; + public: + DEFINE_SIZE_STATIC (sizeof (hb_blob_ptr_t)); }; const SVGDocumentIndexEntry &get_glyph_entry (hb_codepoint_t glyph_id) const @@ -123,4 +148,4 @@ struct SVG_accelerator_t : SVG::accelerator_t { } /* namespace OT */ -#endif /* HB_OT_COLOR_SVG_TABLE_HH */ +#endif /* OT_COLOR_SVG_SVG_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh new file mode 100644 index 00000000000..016a9402e3c --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh @@ -0,0 +1,337 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_COMMON_COVERAGE_HH +#define OT_LAYOUT_COMMON_COVERAGE_HH + +#include "../types.hh" +#include "CoverageFormat1.hh" +#include "CoverageFormat2.hh" + +namespace OT { +namespace Layout { +namespace Common { + +template +static inline void Coverage_serialize (hb_serialize_context_t *c, + Iterator it); + +struct Coverage +{ + + protected: + union { + HBUINT16 format; /* Format identifier */ + CoverageFormat1_3 format1; + CoverageFormat2_4 format2; +#ifndef HB_NO_BEYOND_64K + CoverageFormat1_3format3; + CoverageFormat2_4format4; +#endif + } u; + public: + DEFINE_SIZE_UNION (2, format); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) + { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (u.format3.sanitize (c)); + case 4: return_trace (u.format4.sanitize (c)); +#endif + default:return_trace (true); + } + } + + /* Has interface. */ + unsigned operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != NOT_COVERED; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + unsigned int get (hb_codepoint_t k) const { return get_coverage (k); } + unsigned int get_coverage (hb_codepoint_t glyph_id) const + { + switch (u.format) { + case 1: return u.format1.get_coverage (glyph_id); + case 2: return u.format2.get_coverage (glyph_id); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.get_coverage (glyph_id); + case 4: return u.format4.get_coverage (glyph_id); +#endif + default:return NOT_COVERED; + } + } + + unsigned get_population () const + { + switch (u.format) { + case 1: return u.format1.get_population (); + case 2: return u.format2.get_population (); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.get_population (); + case 4: return u.format4.get_population (); +#endif + default:return NOT_COVERED; + } + } + + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + + unsigned count = 0; + unsigned num_ranges = 0; + hb_codepoint_t last = (hb_codepoint_t) -2; + for (auto g: glyphs) + { + if (last + 1 != g) + num_ranges++; + last = g; + count++; + } + u.format = count <= num_ranges * 3 ? 1 : 2; + +#ifndef HB_NO_BEYOND_64K + if (count && last > 0xFFFFu) + u.format += 2; +#endif + + switch (u.format) + { + case 1: return_trace (u.format1.serialize (c, glyphs)); + case 2: return_trace (u.format2.serialize (c, glyphs)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (u.format3.serialize (c, glyphs)); + case 4: return_trace (u.format4.serialize (c, glyphs)); +#endif + default:return_trace (false); + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto it = + + iter () + | hb_take (c->plan->source->get_num_glyphs ()) + | hb_filter (c->plan->glyph_map_gsub) + | hb_map_retains_sorting (c->plan->glyph_map_gsub) + ; + + // Cache the iterator result as it will be iterated multiple times + // by the serialize code below. + hb_sorted_vector_t glyphs (it); + Coverage_serialize (c->serializer, glyphs.iter ()); + return_trace (bool (glyphs)); + } + + bool intersects (const hb_set_t *glyphs) const + { + switch (u.format) + { + case 1: return u.format1.intersects (glyphs); + case 2: return u.format2.intersects (glyphs); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.intersects (glyphs); + case 4: return u.format4.intersects (glyphs); +#endif + default:return false; + } + } + bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const + { + switch (u.format) + { + case 1: return u.format1.intersects_coverage (glyphs, index); + case 2: return u.format2.intersects_coverage (glyphs, index); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.intersects_coverage (glyphs, index); + case 4: return u.format4.intersects_coverage (glyphs, index); +#endif + default:return false; + } + } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template + bool collect_coverage (set_t *glyphs) const + { + switch (u.format) + { + case 1: return u.format1.collect_coverage (glyphs); + case 2: return u.format2.collect_coverage (glyphs); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.collect_coverage (glyphs); + case 4: return u.format4.collect_coverage (glyphs); +#endif + default:return false; + } + } + + template + void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const + { + switch (u.format) + { + case 1: return u.format1.intersect_set (glyphs, intersect_glyphs); + case 2: return u.format2.intersect_set (glyphs, intersect_glyphs); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.intersect_set (glyphs, intersect_glyphs); + case 4: return u.format4.intersect_set (glyphs, intersect_glyphs); +#endif + default:return ; + } + } + + struct iter_t : hb_iter_with_fallback_t + { + static constexpr bool is_sorted_iterator = true; + iter_t (const Coverage &c_ = Null (Coverage)) + { + hb_memset (this, 0, sizeof (*this)); + format = c_.u.format; + switch (format) + { + case 1: u.format1.init (c_.u.format1); return; + case 2: u.format2.init (c_.u.format2); return; +#ifndef HB_NO_BEYOND_64K + case 3: u.format3.init (c_.u.format3); return; + case 4: u.format4.init (c_.u.format4); return; +#endif + default: return; + } + } + bool __more__ () const + { + switch (format) + { + case 1: return u.format1.__more__ (); + case 2: return u.format2.__more__ (); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.__more__ (); + case 4: return u.format4.__more__ (); +#endif + default:return false; + } + } + void __next__ () + { + switch (format) + { + case 1: u.format1.__next__ (); break; + case 2: u.format2.__next__ (); break; +#ifndef HB_NO_BEYOND_64K + case 3: u.format3.__next__ (); break; + case 4: u.format4.__next__ (); break; +#endif + default: break; + } + } + typedef hb_codepoint_t __item_t__; + __item_t__ __item__ () const { return get_glyph (); } + + hb_codepoint_t get_glyph () const + { + switch (format) + { + case 1: return u.format1.get_glyph (); + case 2: return u.format2.get_glyph (); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.get_glyph (); + case 4: return u.format4.get_glyph (); +#endif + default:return 0; + } + } + bool operator != (const iter_t& o) const + { + if (unlikely (format != o.format)) return true; + switch (format) + { + case 1: return u.format1 != o.u.format1; + case 2: return u.format2 != o.u.format2; +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3 != o.u.format3; + case 4: return u.format4 != o.u.format4; +#endif + default:return false; + } + } + iter_t __end__ () const + { + iter_t it = {}; + it.format = format; + switch (format) + { + case 1: it.u.format1 = u.format1.__end__ (); break; + case 2: it.u.format2 = u.format2.__end__ (); break; +#ifndef HB_NO_BEYOND_64K + case 3: it.u.format3 = u.format3.__end__ (); break; + case 4: it.u.format4 = u.format4.__end__ (); break; +#endif + default: break; + } + return it; + } + + private: + unsigned int format; + union { +#ifndef HB_NO_BEYOND_64K + CoverageFormat2_4::iter_t format4; /* Put this one first since it's larger; helps shut up compiler. */ + CoverageFormat1_3::iter_t format3; +#endif + CoverageFormat2_4::iter_t format2; /* Put this one first since it's larger; helps shut up compiler. */ + CoverageFormat1_3::iter_t format1; + } u; + }; + iter_t iter () const { return iter_t (*this); } +}; + +template +static inline void +Coverage_serialize (hb_serialize_context_t *c, + Iterator it) +{ c->start_embed ()->serialize (c, it); } + +} +} +} + +#endif // #ifndef OT_LAYOUT_COMMON_COVERAGE_HH diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh new file mode 100644 index 00000000000..1fa92df3620 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh @@ -0,0 +1,133 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + + +#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH +#define OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH + +namespace OT { +namespace Layout { +namespace Common { + +#define NOT_COVERED ((unsigned int) -1) + +template +struct CoverageFormat1_3 +{ + friend struct Coverage; + + protected: + HBUINT16 coverageFormat; /* Format identifier--format = 1 */ + SortedArray16Of + glyphArray; /* Array of GlyphIDs--in numerical order */ + public: + DEFINE_SIZE_ARRAY (4, glyphArray); + + private: + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (glyphArray.sanitize (c)); + } + + unsigned int get_coverage (hb_codepoint_t glyph_id) const + { + unsigned int i; + glyphArray.bfind (glyph_id, &i, HB_NOT_FOUND_STORE, NOT_COVERED); + return i; + } + + unsigned get_population () const + { + return glyphArray.len; + } + + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) + { + TRACE_SERIALIZE (this); + return_trace (glyphArray.serialize (c, glyphs)); + } + + bool intersects (const hb_set_t *glyphs) const + { + if (glyphArray.len > glyphs->get_population () * hb_bit_storage ((unsigned) glyphArray.len) / 2) + { + for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);) + if (get_coverage (g) != NOT_COVERED) + return true; + return false; + } + + for (const auto& g : glyphArray.as_array ()) + if (glyphs->has (g)) + return true; + return false; + } + bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const + { return glyphs->has (glyphArray[index]); } + + template + void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const + { + unsigned count = glyphArray.len; + for (unsigned i = 0; i < count; i++) + if (glyphs.has (glyphArray[i])) + intersect_glyphs << glyphArray[i]; + } + + template + bool collect_coverage (set_t *glyphs) const + { return glyphs->add_sorted_array (glyphArray.as_array ()); } + + public: + /* Older compilers need this to be public. */ + struct iter_t + { + void init (const struct CoverageFormat1_3 &c_) { c = &c_; i = 0; } + bool __more__ () const { return i < c->glyphArray.len; } + void __next__ () { i++; } + hb_codepoint_t get_glyph () const { return c->glyphArray[i]; } + bool operator != (const iter_t& o) const + { return i != o.i; } + iter_t __end__ () const { iter_t it; it.init (*c); it.i = c->glyphArray.len; return it; } + + private: + const struct CoverageFormat1_3 *c; + unsigned int i; + }; + private: +}; + +} +} +} + +#endif // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh new file mode 100644 index 00000000000..2650d198f4c --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh @@ -0,0 +1,232 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH +#define OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH + +#include "RangeRecord.hh" + +namespace OT { +namespace Layout { +namespace Common { + +template +struct CoverageFormat2_4 +{ + friend struct Coverage; + + protected: + HBUINT16 coverageFormat; /* Format identifier--format = 2 */ + SortedArray16Of> + rangeRecord; /* Array of glyph ranges--ordered by + * Start GlyphID. rangeCount entries + * long */ + public: + DEFINE_SIZE_ARRAY (4, rangeRecord); + + private: + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (rangeRecord.sanitize (c)); + } + + unsigned int get_coverage (hb_codepoint_t glyph_id) const + { + const RangeRecord &range = rangeRecord.bsearch (glyph_id); + return likely (range.first <= range.last) + ? (unsigned int) range.value + (glyph_id - range.first) + : NOT_COVERED; + } + + unsigned get_population () const + { + typename Types::large_int ret = 0; + for (const auto &r : rangeRecord) + ret += r.get_population (); + return ret > UINT_MAX ? UINT_MAX : (unsigned) ret; + } + + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + + unsigned num_ranges = 0; + hb_codepoint_t last = (hb_codepoint_t) -2; + for (auto g: glyphs) + { + if (last + 1 != g) + num_ranges++; + last = g; + } + + if (unlikely (!rangeRecord.serialize (c, num_ranges))) return_trace (false); + if (!num_ranges) return_trace (true); + + unsigned count = 0; + unsigned range = (unsigned) -1; + last = (hb_codepoint_t) -2; + for (auto g: glyphs) + { + if (last + 1 != g) + { + range++; + rangeRecord[range].first = g; + rangeRecord[range].value = count; + } + rangeRecord[range].last = g; + last = g; + count++; + } + + return_trace (true); + } + + bool intersects (const hb_set_t *glyphs) const + { + if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2) + { + for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);) + if (get_coverage (g) != NOT_COVERED) + return true; + return false; + } + + return hb_any (+ hb_iter (rangeRecord) + | hb_map ([glyphs] (const RangeRecord &range) { return range.intersects (*glyphs); })); + } + bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const + { + auto *range = rangeRecord.as_array ().bsearch (index); + if (range) + return range->intersects (*glyphs); + return false; + } + + template + void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const + { + /* Break out of loop for overlapping, broken, tables, + * to avoid fuzzer timouts. */ + hb_codepoint_t last = 0; + for (const auto& range : rangeRecord) + { + if (unlikely (range.first < last)) + break; + last = range.last; + for (hb_codepoint_t g = range.first - 1; + glyphs.next (&g) && g <= last;) + intersect_glyphs << g; + } + } + + template + bool collect_coverage (set_t *glyphs) const + { + for (const auto& range: rangeRecord) + if (unlikely (!range.collect_coverage (glyphs))) + return false; + return true; + } + + public: + /* Older compilers need this to be public. */ + struct iter_t + { + void init (const CoverageFormat2_4 &c_) + { + c = &c_; + coverage = 0; + i = 0; + j = c->rangeRecord.len ? c->rangeRecord[0].first : 0; + if (unlikely (c->rangeRecord[0].first > c->rangeRecord[0].last)) + { + /* Broken table. Skip. */ + i = c->rangeRecord.len; + j = 0; + } + } + bool __more__ () const { return i < c->rangeRecord.len; } + void __next__ () + { + if (j >= c->rangeRecord[i].last) + { + i++; + if (__more__ ()) + { + unsigned int old = coverage; + j = c->rangeRecord[i].first; + coverage = c->rangeRecord[i].value; + if (unlikely (coverage != old + 1)) + { + /* Broken table. Skip. Important to avoid DoS. + * Also, our callers depend on coverage being + * consecutive and monotonically increasing, + * ie. iota(). */ + i = c->rangeRecord.len; + j = 0; + return; + } + } + else + j = 0; + return; + } + coverage++; + j++; + } + hb_codepoint_t get_glyph () const { return j; } + bool operator != (const iter_t& o) const + { return i != o.i || j != o.j; } + iter_t __end__ () const + { + iter_t it; + it.init (*c); + it.i = c->rangeRecord.len; + it.j = 0; + return it; + } + + private: + const struct CoverageFormat2_4 *c; + unsigned int i, coverage; + hb_codepoint_t j; + }; + private: +}; + +} +} +} + +#endif // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/RangeRecord.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/RangeRecord.hh new file mode 100644 index 00000000000..a62629fad34 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/RangeRecord.hh @@ -0,0 +1,85 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_COMMON_RANGERECORD_HH +#define OT_LAYOUT_COMMON_RANGERECORD_HH + +namespace OT { +namespace Layout { +namespace Common { + +template +struct RangeRecord +{ + typename Types::HBGlyphID first; /* First GlyphID in the range */ + typename Types::HBGlyphID last; /* Last GlyphID in the range */ + HBUINT16 value; /* Value */ + + DEFINE_SIZE_STATIC (2 + 2 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + int cmp (hb_codepoint_t g) const + { return g < first ? -1 : g <= last ? 0 : +1; } + + unsigned get_population () const + { + if (unlikely (last < first)) return 0; + return (last - first + 1); + } + + bool intersects (const hb_set_t &glyphs) const + { return glyphs.intersects (first, last); } + + template + bool collect_coverage (set_t *glyphs) const + { return glyphs->add_range (first, last); } +}; + +} +} +} + +// TODO(garretrieger): This was previously implemented using +// DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (OT, RangeRecord, 9); +// but that only works when there is only a single namespace level. +// The macro should probably be fixed so it can work in this situation. +extern HB_INTERNAL const unsigned char _hb_Null_OT_RangeRecord[9]; +template +struct Null> { + static OT::Layout::Common::RangeRecord const & get_null () { + return *reinterpret_cast *> (_hb_Null_OT_RangeRecord); + } +}; + + +#endif // #ifndef OT_LAYOUT_COMMON_RANGERECORD_HH diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh new file mode 100644 index 00000000000..0cf5753b0e5 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh @@ -0,0 +1,918 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef OT_LAYOUT_GDEF_GDEF_HH +#define OT_LAYOUT_GDEF_GDEF_HH + +#include "../../../hb-ot-layout-common.hh" + +#include "../../../hb-font.hh" + + +namespace OT { + + +/* + * Attachment List Table + */ + +/* Array of contour point indices--in increasing numerical order */ +struct AttachPoint : Array16Of +{ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->serialize (c->serializer, + iter ())); + } +}; + +struct AttachList +{ + unsigned int get_attach_points (hb_codepoint_t glyph_id, + unsigned int start_offset, + unsigned int *point_count /* IN/OUT */, + unsigned int *point_array /* OUT */) const + { + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (index == NOT_COVERED) + { + if (point_count) + *point_count = 0; + return 0; + } + + const AttachPoint &points = this+attachPoint[index]; + + if (point_count) + { + + points.as_array ().sub_array (start_offset, point_count) + | hb_sink (hb_array (point_array, *point_count)) + ; + } + + return points.len; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, attachPoint) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->attachPoint, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && attachPoint.sanitize (c, this)); + } + + protected: + Offset16To + coverage; /* Offset to Coverage table -- from + * beginning of AttachList table */ + Array16OfOffset16To + attachPoint; /* Array of AttachPoint tables + * in Coverage Index order */ + public: + DEFINE_SIZE_ARRAY (4, attachPoint); +}; + +/* + * Ligature Caret Table + */ + +struct CaretValueFormat1 +{ + friend struct CaretValue; + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (true); + } + + private: + hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction) const + { + return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_x (coordinate) : font->em_scale_y (coordinate); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 caretValueFormat; /* Format identifier--format = 1 */ + FWORD coordinate; /* X or Y value, in design units */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct CaretValueFormat2 +{ + friend struct CaretValue; + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (true); + } + + private: + hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id) const + { + hb_position_t x, y; + font->get_glyph_contour_point_for_origin (glyph_id, caretValuePoint, direction, &x, &y); + return HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 caretValueFormat; /* Format identifier--format = 2 */ + HBUINT16 caretValuePoint; /* Contour point index on glyph */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct CaretValueFormat3 +{ + friend struct CaretValue; + + hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, + const VariationStore &var_store) const + { + return HB_DIRECTION_IS_HORIZONTAL (direction) ? + font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font, var_store) : + font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font, var_store); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out)) return_trace (false); + if (!c->serializer->embed (caretValueFormat)) return_trace (false); + if (!c->serializer->embed (coordinate)) return_trace (false); + + unsigned varidx = (this+deviceTable).get_variation_index (); + if (c->plan->layout_variation_idx_delta_map.has (varidx)) + { + int delta = hb_second (c->plan->layout_variation_idx_delta_map.get (varidx)); + if (delta != 0) + { + if (!c->serializer->check_assign (out->coordinate, coordinate + delta, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + } + } + + if (c->plan->all_axes_pinned) + return_trace (c->serializer->check_assign (out->caretValueFormat, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + + if (!c->serializer->embed (deviceTable)) + return_trace (false); + + return_trace (out->deviceTable.serialize_copy (c->serializer, deviceTable, this, c->serializer->to_bias (out), + hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { (this+deviceTable).collect_variation_indices (c); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && deviceTable.sanitize (c, this)); + } + + protected: + HBUINT16 caretValueFormat; /* Format identifier--format = 3 */ + FWORD coordinate; /* X or Y value, in design units */ + Offset16To + deviceTable; /* Offset to Device table for X or Y + * value--from beginning of CaretValue + * table */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct CaretValue +{ + hb_position_t get_caret_value (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store) const + { + switch (u.format) { + case 1: return u.format1.get_caret_value (font, direction); + case 2: return u.format2.get_caret_value (font, direction, glyph_id); + case 3: return u.format3.get_caret_value (font, direction, var_store); + default:return 0; + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); + case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + switch (u.format) { + case 1: + case 2: + return; + case 3: + u.format3.collect_variation_indices (c); + return; + default: return; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + HBUINT16 format; /* Format identifier */ + CaretValueFormat1 format1; + CaretValueFormat2 format2; + CaretValueFormat3 format3; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + +struct LigGlyph +{ + unsigned get_lig_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store, + unsigned start_offset, + unsigned *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */) const + { + if (caret_count) + { + + carets.as_array ().sub_array (start_offset, caret_count) + | hb_map (hb_add (this)) + | hb_map ([&] (const CaretValue &value) { return value.get_caret_value (font, direction, glyph_id, var_store); }) + | hb_sink (hb_array (caret_array, *caret_count)) + ; + } + + return carets.len; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + hb_iter (carets) + | hb_apply (subset_offset_array (c, out->carets, this)) + ; + + return_trace (bool (out->carets)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + for (const Offset16To& offset : carets.iter ()) + (this+offset).collect_variation_indices (c); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (carets.sanitize (c, this)); + } + + protected: + Array16OfOffset16To + carets; /* Offset array of CaretValue tables + * --from beginning of LigGlyph table + * --in increasing coordinate order */ + public: + DEFINE_SIZE_ARRAY (2, carets); +}; + +struct LigCaretList +{ + unsigned int get_lig_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store, + unsigned int start_offset, + unsigned int *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */) const + { + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (index == NOT_COVERED) + { + if (caret_count) + *caret_count = 0; + return 0; + } + const LigGlyph &lig_glyph = this+ligGlyph[index]; + return lig_glyph.get_lig_carets (font, direction, glyph_id, var_store, start_offset, caret_count, caret_array); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, ligGlyph) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->ligGlyph, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+coverage, ligGlyph) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const LigGlyph& _) { _.collect_variation_indices (c); }) + ; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this)); + } + + protected: + Offset16To + coverage; /* Offset to Coverage table--from + * beginning of LigCaretList table */ + Array16OfOffset16To + ligGlyph; /* Array of LigGlyph tables + * in Coverage Index order */ + public: + DEFINE_SIZE_ARRAY (4, ligGlyph); +}; + + +struct MarkGlyphSetsFormat1 +{ + bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const + { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + bool ret = true; + for (const Offset32To& offset : coverage.iter ()) + { + auto *o = out->coverage.serialize_append (c->serializer); + if (unlikely (!o)) + { + ret = false; + break; + } + + //not using o->serialize_subset (c, offset, this, out) here because + //OTS doesn't allow null offset. + //See issue: https://github.com/khaledhosny/ots/issues/172 + c->serializer->push (); + c->dispatch (this+offset); + c->serializer->add_link (*o, c->serializer->pop_pack ()); + } + + return_trace (ret && out->coverage.len); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Array16Of> + coverage; /* Array of long offsets to mark set + * coverage tables */ + public: + DEFINE_SIZE_ARRAY (4, coverage); +}; + +struct MarkGlyphSets +{ + bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const + { + switch (u.format) { + case 1: return u.format1.covers (set_index, glyph_id); + default:return false; + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + switch (u.format) { + case 1: return_trace (u.format1.subset (c)); + default:return_trace (false); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + HBUINT16 format; /* Format identifier */ + MarkGlyphSetsFormat1 format1; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + + +/* + * GDEF -- Glyph Definition + * https://docs.microsoft.com/en-us/typography/opentype/spec/gdef + */ + + +template +struct GDEFVersion1_2 +{ + friend struct GDEF; + + protected: + FixedVersion<>version; /* Version of the GDEF table--currently + * 0x00010003u */ + typename Types::template OffsetTo + glyphClassDef; /* Offset to class definition table + * for glyph type--from beginning of + * GDEF header (may be Null) */ + typename Types::template OffsetTo + attachList; /* Offset to list of glyphs with + * attachment points--from beginning + * of GDEF header (may be Null) */ + typename Types::template OffsetTo + ligCaretList; /* Offset to list of positioning points + * for ligature carets--from beginning + * of GDEF header (may be Null) */ + typename Types::template OffsetTo + markAttachClassDef; /* Offset to class definition table for + * mark attachment type--from beginning + * of GDEF header (may be Null) */ + typename Types::template OffsetTo + markGlyphSetsDef; /* Offset to the table of mark set + * definitions--from beginning of GDEF + * header (may be NULL). Introduced + * in version 0x00010002. */ + Offset32To + varStore; /* Offset to the table of Item Variation + * Store--from beginning of GDEF + * header (may be NULL). Introduced + * in version 0x00010003. */ + public: + DEFINE_SIZE_MIN (4 + 4 * Types::size); + + unsigned int get_size () const + { + return min_size + + (version.to_int () >= 0x00010002u ? markGlyphSetsDef.static_size : 0) + + (version.to_int () >= 0x00010003u ? varStore.static_size : 0); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + glyphClassDef.sanitize (c, this) && + attachList.sanitize (c, this) && + ligCaretList.sanitize (c, this) && + markAttachClassDef.sanitize (c, this) && + (version.to_int () < 0x00010002u || markGlyphSetsDef.sanitize (c, this)) && + (version.to_int () < 0x00010003u || varStore.sanitize (c, this))); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this, nullptr, false, true); + bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this); + bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this); + bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true); + + bool subset_markglyphsetsdef = false; + if (version.to_int () >= 0x00010002u) + { + subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this); + } + + bool subset_varstore = false; + if (version.to_int () >= 0x00010003u) + { + if (c->plan->all_axes_pinned) + out->varStore = 0; + else + subset_varstore = out->varStore.serialize_subset (c, varStore, this, c->plan->gdef_varstore_inner_maps.as_array ()); + } + + if (subset_varstore) + { + out->version.minor = 3; + } else if (subset_markglyphsetsdef) { + out->version.minor = 2; + } else { + out->version.minor = 0; + } + + return_trace (subset_glyphclassdef || subset_attachlist || + subset_ligcaretlist || subset_markattachclassdef || + (out->version.to_int () >= 0x00010002u && subset_markglyphsetsdef) || + (out->version.to_int () >= 0x00010003u && subset_varstore)); + } +}; + +struct GDEF +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_GDEF; + + enum GlyphClasses { + UnclassifiedGlyph = 0, + BaseGlyph = 1, + LigatureGlyph = 2, + MarkGlyph = 3, + ComponentGlyph = 4 + }; + + unsigned int get_size () const + { + switch (u.version.major) { + case 1: return u.version1.get_size (); +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.get_size (); +#endif + default: return u.version.static_size; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.version.sanitize (c))) return_trace (false); + switch (u.version.major) { + case 1: return_trace (u.version1.sanitize (c)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (u.version2.sanitize (c)); +#endif + default: return_trace (true); + } + } + + bool subset (hb_subset_context_t *c) const + { + switch (u.version.major) { + case 1: return u.version1.subset (c); +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.subset (c); +#endif + default: return false; + } + } + + bool has_glyph_classes () const + { + switch (u.version.major) { + case 1: return u.version1.glyphClassDef != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.glyphClassDef != 0; +#endif + default: return false; + } + } + const ClassDef &get_glyph_class_def () const + { + switch (u.version.major) { + case 1: return this+u.version1.glyphClassDef; +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.glyphClassDef; +#endif + default: return Null(ClassDef); + } + } + bool has_attach_list () const + { + switch (u.version.major) { + case 1: return u.version1.attachList != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.attachList != 0; +#endif + default: return false; + } + } + const AttachList &get_attach_list () const + { + switch (u.version.major) { + case 1: return this+u.version1.attachList; +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.attachList; +#endif + default: return Null(AttachList); + } + } + bool has_lig_carets () const + { + switch (u.version.major) { + case 1: return u.version1.ligCaretList != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.ligCaretList != 0; +#endif + default: return false; + } + } + const LigCaretList &get_lig_caret_list () const + { + switch (u.version.major) { + case 1: return this+u.version1.ligCaretList; +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.ligCaretList; +#endif + default: return Null(LigCaretList); + } + } + bool has_mark_attachment_types () const + { + switch (u.version.major) { + case 1: return u.version1.markAttachClassDef != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.markAttachClassDef != 0; +#endif + default: return false; + } + } + const ClassDef &get_mark_attach_class_def () const + { + switch (u.version.major) { + case 1: return this+u.version1.markAttachClassDef; +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.markAttachClassDef; +#endif + default: return Null(ClassDef); + } + } + bool has_mark_glyph_sets () const + { + switch (u.version.major) { + case 1: return u.version.to_int () >= 0x00010002u && u.version1.markGlyphSetsDef != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.markGlyphSetsDef != 0; +#endif + default: return false; + } + } + const MarkGlyphSets &get_mark_glyph_sets () const + { + switch (u.version.major) { + case 1: return u.version.to_int () >= 0x00010002u ? this+u.version1.markGlyphSetsDef : Null(MarkGlyphSets); +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.markGlyphSetsDef; +#endif + default: return Null(MarkGlyphSets); + } + } + bool has_var_store () const + { + switch (u.version.major) { + case 1: return u.version.to_int () >= 0x00010003u && u.version1.varStore != 0; +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.varStore != 0; +#endif + default: return false; + } + } + const VariationStore &get_var_store () const + { + switch (u.version.major) { + case 1: return u.version.to_int () >= 0x00010003u ? this+u.version1.varStore : Null(VariationStore); +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.varStore; +#endif + default: return Null(VariationStore); + } + } + + + bool has_data () const { return u.version.to_int (); } + unsigned int get_glyph_class (hb_codepoint_t glyph) const + { return get_glyph_class_def ().get_class (glyph); } + void get_glyphs_in_class (unsigned int klass, hb_set_t *glyphs) const + { get_glyph_class_def ().collect_class (glyphs, klass); } + + unsigned int get_mark_attachment_type (hb_codepoint_t glyph) const + { return get_mark_attach_class_def ().get_class (glyph); } + + unsigned int get_attach_points (hb_codepoint_t glyph_id, + unsigned int start_offset, + unsigned int *point_count /* IN/OUT */, + unsigned int *point_array /* OUT */) const + { return get_attach_list ().get_attach_points (glyph_id, start_offset, point_count, point_array); } + + unsigned int get_lig_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + unsigned int start_offset, + unsigned int *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */) const + { return get_lig_caret_list ().get_lig_carets (font, + direction, glyph_id, get_var_store(), + start_offset, caret_count, caret_array); } + + bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const + { return get_mark_glyph_sets ().covers (set_index, glyph_id); } + + /* glyph_props is a 16-bit integer where the lower 8-bit have bits representing + * glyph class and other bits, and high 8-bit the mark attachment type (if any). + * Not to be confused with lookup_props which is very similar. */ + unsigned int get_glyph_props (hb_codepoint_t glyph) const + { + unsigned int klass = get_glyph_class (glyph); + + static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH == (unsigned int) LookupFlag::IgnoreBaseGlyphs), ""); + static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE == (unsigned int) LookupFlag::IgnoreLigatures), ""); + static_assert (((unsigned int) HB_OT_LAYOUT_GLYPH_PROPS_MARK == (unsigned int) LookupFlag::IgnoreMarks), ""); + + switch (klass) { + default: return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED; + case BaseGlyph: return HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH; + case LigatureGlyph: return HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE; + case MarkGlyph: + klass = get_mark_attachment_type (glyph); + return HB_OT_LAYOUT_GLYPH_PROPS_MARK | (klass << 8); + } + } + + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, + hb_face_t *face) const; + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + table = hb_sanitize_context_t ().reference_table (face); + if (unlikely (table->is_blocklisted (table.get_blob (), face))) + { + hb_blob_destroy (table.get_blob ()); + table = hb_blob_get_empty (); + } + } + ~accelerator_t () { table.destroy (); } + + hb_blob_ptr_t table; + }; + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { get_lig_caret_list ().collect_variation_indices (c); } + + void remap_layout_variation_indices (const hb_set_t *layout_variation_indices, + hb_hashmap_t> *layout_variation_idx_delta_map /* OUT */) const + { + if (!has_var_store ()) return; + if (layout_variation_indices->is_empty ()) return; + + unsigned new_major = 0, new_minor = 0; + unsigned last_major = (layout_variation_indices->get_min ()) >> 16; + for (unsigned idx : layout_variation_indices->iter ()) + { + uint16_t major = idx >> 16; + if (major >= get_var_store ().get_sub_table_count ()) break; + if (major != last_major) + { + new_minor = 0; + ++new_major; + } + + unsigned new_idx = (new_major << 16) + new_minor; + if (!layout_variation_idx_delta_map->has (idx)) + continue; + int delta = hb_second (layout_variation_idx_delta_map->get (idx)); + + layout_variation_idx_delta_map->set (idx, hb_pair_t (new_idx, delta)); + ++new_minor; + last_major = major; + } + } + + protected: + union { + FixedVersion<> version; /* Version identifier */ + GDEFVersion1_2 version1; +#ifndef HB_NO_BEYOND_64K + GDEFVersion1_2 version2; +#endif + } u; + public: + DEFINE_SIZE_MIN (4); +}; + +struct GDEF_accelerator_t : GDEF::accelerator_t { + GDEF_accelerator_t (hb_face_t *face) : GDEF::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* OT_LAYOUT_GDEF_GDEF_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh index bfe6b36afd3..49e76e77509 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh @@ -58,8 +58,7 @@ struct Anchor return_trace (bool (reinterpret_cast (u.format1.copy (c->serializer)))); } return_trace (bool (reinterpret_cast (u.format2.copy (c->serializer)))); - case 3: return_trace (bool (reinterpret_cast (u.format3.copy (c->serializer, - c->plan->layout_variation_idx_map)))); + case 3: return_trace (u.format3.subset (c)); default:return_trace (false); } } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh index d77b4699be9..e7e3c5c6d1e 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh @@ -41,24 +41,54 @@ struct AnchorFormat3 *y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache); } - AnchorFormat3* copy (hb_serialize_context_t *c, - const hb_map_t *layout_variation_idx_map) const + bool subset (hb_subset_context_t *c) const { - TRACE_SERIALIZE (this); - if (!layout_variation_idx_map) return_trace (nullptr); + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out)) return_trace (false); + if (unlikely (!c->serializer->embed (format))) return_trace (false); + if (unlikely (!c->serializer->embed (xCoordinate))) return_trace (false); + if (unlikely (!c->serializer->embed (yCoordinate))) return_trace (false); - auto *out = c->embed (this); - if (unlikely (!out)) return_trace (nullptr); + unsigned x_varidx = xDeviceTable ? (this+xDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX; + if (c->plan->layout_variation_idx_delta_map.has (x_varidx)) + { + int delta = hb_second (c->plan->layout_variation_idx_delta_map.get (x_varidx)); + if (delta != 0) + { + if (!c->serializer->check_assign (out->xCoordinate, xCoordinate + delta, + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + } + } - out->xDeviceTable.serialize_copy (c, xDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map); - out->yDeviceTable.serialize_copy (c, yDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map); + unsigned y_varidx = yDeviceTable ? (this+yDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX; + if (c->plan->layout_variation_idx_delta_map.has (y_varidx)) + { + int delta = hb_second (c->plan->layout_variation_idx_delta_map.get (y_varidx)); + if (delta != 0) + { + if (!c->serializer->check_assign (out->yCoordinate, yCoordinate + delta, + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + } + } + + if (c->plan->all_axes_pinned) + return_trace (c->serializer->check_assign (out->format, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + + if (!c->serializer->embed (xDeviceTable)) return_trace (false); + if (!c->serializer->embed (yDeviceTable)) return_trace (false); + + out->xDeviceTable.serialize_copy (c->serializer, xDeviceTable, this, 0, hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map); + out->yDeviceTable.serialize_copy (c->serializer, yDeviceTable, this, 0, hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map); return_trace (out); } void collect_variation_indices (hb_collect_variation_indices_context_t *c) const { - (this+xDeviceTable).collect_variation_indices (c->layout_variation_indices); - (this+yDeviceTable).collect_variation_indices (c->layout_variation_indices); + (this+xDeviceTable).collect_variation_indices (c); + (this+yDeviceTable).collect_variation_indices (c); } }; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Common.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Common.hh index e16c06729d2..408197454f1 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Common.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Common.hh @@ -22,7 +22,8 @@ template static void SinglePos_serialize (hb_serialize_context_t *c, const SrcLookup *src, Iterator it, - const hb_map_t *layout_variation_idx_map); + const hb_hashmap_t> *layout_variation_idx_delta_map, + bool all_axes_pinned); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePos.hh index c105cfb0916..0105a9b8542 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePos.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePos.hh @@ -19,8 +19,8 @@ struct CursivePos template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); - if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); default:return_trace (c->default_return_value ()); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh index e212fab976b..13a435d00bf 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh @@ -140,7 +140,14 @@ struct CursivePosFormat1 unsigned int i = skippy_iter.idx; unsigned int j = buffer->idx; - buffer->unsafe_to_break (i, j); + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "cursive attaching glyph at %u to glyph at %u", + i, j); + } + + buffer->unsafe_to_break (i, j + 1); float entry_x, entry_y, exit_x, exit_y; (this+prev_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y); (this+this_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y); @@ -223,7 +230,20 @@ struct CursivePosFormat1 * https://github.com/harfbuzz/harfbuzz/issues/2469 */ if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain())) + { pos[parent].attach_chain() = 0; + if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) + pos[parent].y_offset = 0; + else + pos[parent].x_offset = 0; + } + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "cursive attached glyph at %u to glyph at %u", + i, j); + } buffer->idx++; return_trace (true); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh similarity index 84% rename from src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS.hh rename to src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh index 224d6b746bd..9493ec987e8 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh @@ -1,12 +1,15 @@ -#ifndef OT_LAYOUT_GPOS_HH -#define OT_LAYOUT_GPOS_HH +#ifndef OT_LAYOUT_GPOS_GPOS_HH +#define OT_LAYOUT_GPOS_GPOS_HH -#include "../../hb-ot-layout-common.hh" -#include "../../hb-ot-layout-gsubgpos.hh" -#include "GPOS/Common.hh" -#include "GPOS/PosLookup.hh" +#include "../../../hb-ot-layout-common.hh" +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" +#include "PosLookup.hh" namespace OT { + +using Layout::GPOS_impl::PosLookup; + namespace Layout { static void @@ -25,10 +28,10 @@ struct GPOS : GSUBGPOS { static constexpr hb_tag_t tableTag = HB_OT_TAG_GPOS; - using Lookup = GPOS_impl::PosLookup; + using Lookup = PosLookup; - const GPOS_impl::PosLookup& get_lookup (unsigned int i) const - { return static_cast (GSUBGPOS::get_lookup (i)); } + const PosLookup& get_lookup (unsigned int i) const + { return static_cast (GSUBGPOS::get_lookup (i)); } static inline void position_start (hb_font_t *font, hb_buffer_t *buffer); static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer); @@ -36,12 +39,15 @@ struct GPOS : GSUBGPOS bool subset (hb_subset_context_t *c) const { - hb_subset_layout_context_t l (c, tableTag, c->plan->gpos_lookups, c->plan->gpos_langsys, c->plan->gpos_features); - return GSUBGPOS::subset (&l); + hb_subset_layout_context_t l (c, tableTag); + return GSUBGPOS::subset (&l); } bool sanitize (hb_sanitize_context_t *c) const - { return GSUBGPOS::sanitize (c); } + { + TRACE_SANITIZE (this); + return_trace (GSUBGPOS::sanitize (c)); + } HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, hb_face_t *face) const; @@ -51,7 +57,7 @@ struct GPOS : GSUBGPOS for (unsigned i = 0; i < GSUBGPOS::get_lookup_count (); i++) { if (!c->gpos_lookups->has (i)) continue; - const GPOS_impl::PosLookup &l = get_lookup (i); + const PosLookup &l = get_lookup (i); l.dispatch (c); } } @@ -59,7 +65,7 @@ struct GPOS : GSUBGPOS void closure_lookups (hb_face_t *face, const hb_set_t *glyphs, hb_set_t *lookup_indexes /* IN/OUT */) const - { GSUBGPOS::closure_lookups (face, glyphs, lookup_indexes); } + { GSUBGPOS::closure_lookups (face, glyphs, lookup_indexes); } typedef GSUBGPOS::accelerator_t accelerator_t; }; @@ -162,4 +168,4 @@ struct GPOS_accelerator_t : Layout::GPOS::accelerator_t { } -#endif /* OT_LAYOUT_GPOS_HH */ +#endif /* OT_LAYOUT_GPOS_GPOS_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/LigatureArray.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/LigatureArray.hh new file mode 100644 index 00000000000..a2d807cc320 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/LigatureArray.hh @@ -0,0 +1,56 @@ +#ifndef OT_LAYOUT_GPOS_LIGATUREARRAY_HH +#define OT_LAYOUT_GPOS_LIGATUREARRAY_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +typedef AnchorMatrix LigatureAttach; /* component-major-- + * in order of writing direction--, + * mark-minor-- + * ordered by class--zero-based. */ + +/* Array of LigatureAttach tables ordered by LigatureCoverage Index */ +struct LigatureArray : List16OfOffset16To +{ + template + bool subset (hb_subset_context_t *c, + Iterator coverage, + unsigned class_count, + const hb_map_t *klass_mapping) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + for (const auto _ : + hb_zip (coverage, *this) + | hb_filter (glyphset, hb_first)) + { + auto *matrix = out->serialize_append (c->serializer); + if (unlikely (!matrix)) return_trace (false); + + const LigatureAttach& src = (this + _.second); + auto indexes = + + hb_range (src.rows * class_count) + | hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); }) + ; + matrix->serialize_subset (c, + _.second, + this, + src.rows, + indexes); + } + return_trace (this->len); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_LIGATUREARRAY_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkArray.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkArray.hh index f8cddd19918..e80c05cd270 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkArray.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkArray.hh @@ -39,6 +39,13 @@ struct MarkArray : Array16Of /* Array of MarkRecords--in Cove mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y); glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y); + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "attaching mark glyph at %u to glyph at %u", + c->buffer->idx, glyph_pos); + } + hb_glyph_position_t &o = buffer->cur_pos(); o.x_offset = roundf (base_x - mark_x); o.y_offset = roundf (base_y - mark_y); @@ -46,6 +53,13 @@ struct MarkArray : Array16Of /* Array of MarkRecords--in Cove o.attach_chain() = (int) glyph_pos - (int) buffer->idx; buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "attached mark glyph at %u to glyph at %u", + c->buffer->idx, glyph_pos); + } + buffer->idx++; return_trace (true); } @@ -83,10 +97,11 @@ struct MarkArray : Array16Of /* Array of MarkRecords--in Cove } }; -static void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage, - const MarkArray &mark_array, - const hb_set_t &glyphset, - hb_map_t* klass_mapping /* INOUT */) +HB_INTERNAL inline +void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage, + const MarkArray &mark_array, + const hb_set_t &glyphset, + hb_map_t* klass_mapping /* INOUT */) { hb_set_t orig_classes; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePos.hh index e99e13ff84f..b1d1118a86b 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePos.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePos.hh @@ -11,18 +11,24 @@ struct MarkBasePos { protected: union { - HBUINT16 format; /* Format identifier */ - MarkBasePosFormat1 format1; + HBUINT16 format; /* Format identifier */ + MarkBasePosFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + MarkBasePosFormat1_2 format2; +#endif } u; public: template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); - if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif default:return_trace (c->default_return_value ()); } } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePosFormat1.hh index a10b806fe5f..bf129a4a0e9 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePosFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePosFormat1.hh @@ -12,26 +12,27 @@ typedef AnchorMatrix BaseArray; /* base-major-- * mark-minor-- * ordered by class--zero-based. */ -struct MarkBasePosFormat1 +template +struct MarkBasePosFormat1_2 { protected: HBUINT16 format; /* Format identifier--format = 1 */ - Offset16To + typename Types::template OffsetTo markCoverage; /* Offset to MarkCoverage table--from * beginning of MarkBasePos subtable */ - Offset16To + typename Types::template OffsetTo baseCoverage; /* Offset to BaseCoverage table--from * beginning of MarkBasePos subtable */ HBUINT16 classCount; /* Number of classes defined for marks */ - Offset16To + typename Types::template OffsetTo markArray; /* Offset to MarkArray table--from * beginning of MarkBasePos subtable */ - Offset16To + typename Types::template OffsetTo baseArray; /* Offset to BaseArray table--from * beginning of MarkBasePos subtable */ public: - DEFINE_SIZE_STATIC (12); + DEFINE_SIZE_STATIC (4 + 4 * Types::size); bool sanitize (hb_sanitize_context_t *c) const { @@ -89,6 +90,25 @@ struct MarkBasePosFormat1 const Coverage &get_coverage () const { return this+markCoverage; } + static inline bool accept (hb_buffer_t *buffer, unsigned idx) + { + /* We only want to attach to the first of a MultipleSubst sequence. + * https://github.com/harfbuzz/harfbuzz/issues/740 + * Reject others... + * ...but stop if we find a mark in the MultipleSubst sequence: + * https://github.com/harfbuzz/harfbuzz/issues/1020 */ + return !_hb_glyph_info_multiplied (&buffer->info[idx]) || + 0 == _hb_glyph_info_get_lig_comp (&buffer->info[idx]) || + (idx == 0 || + _hb_glyph_info_is_mark (&buffer->info[idx - 1]) || + !_hb_glyph_info_multiplied (&buffer->info[idx - 1]) || + _hb_glyph_info_get_lig_id (&buffer->info[idx]) != + _hb_glyph_info_get_lig_id (&buffer->info[idx - 1]) || + _hb_glyph_info_get_lig_comp (&buffer->info[idx]) != + _hb_glyph_info_get_lig_comp (&buffer->info[idx - 1]) + 1 + ); + } + bool apply (hb_ot_apply_context_t *c) const { TRACE_APPLY (this); @@ -96,47 +116,54 @@ struct MarkBasePosFormat1 unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint); if (likely (mark_index == NOT_COVERED)) return_trace (false); - /* Now we search backwards for a non-mark glyph */ + /* Now we search backwards for a non-mark glyph. + * We don't use skippy_iter.prev() to avoid O(n^2) behavior. */ + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; - skippy_iter.reset (buffer->idx, 1); skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); - do { - unsigned unsafe_from; - if (!skippy_iter.prev (&unsafe_from)) + + if (c->last_base_until > buffer->idx) + { + c->last_base_until = 0; + c->last_base = -1; + } + unsigned j; + for (j = buffer->idx; j > c->last_base_until; j--) + { + auto match = skippy_iter.match (buffer->info[j - 1]); + if (match == skippy_iter.MATCH) { - buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1); - return_trace (false); + // https://github.com/harfbuzz/harfbuzz/issues/4124 + if (!accept (buffer, j - 1) && + NOT_COVERED == (this+baseCoverage).get_coverage (buffer->info[j - 1].codepoint)) + match = skippy_iter.SKIP; } - - /* We only want to attach to the first of a MultipleSubst sequence. - * https://github.com/harfbuzz/harfbuzz/issues/740 - * Reject others... - * ...but stop if we find a mark in the MultipleSubst sequence: - * https://github.com/harfbuzz/harfbuzz/issues/1020 */ - if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) || - 0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) || - (skippy_iter.idx == 0 || - _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx - 1]) || - _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) != - _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) || - _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) != - _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1 - )) + if (match == skippy_iter.MATCH) + { + c->last_base = (signed) j - 1; break; - skippy_iter.reject (); - } while (true); + } + } + c->last_base_until = buffer->idx; + if (c->last_base == -1) + { + buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1); + return_trace (false); + } + + unsigned idx = (unsigned) c->last_base; /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */ - //if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); } + //if (!_hb_glyph_info_is_base_glyph (&buffer->info[idx])) { return_trace (false); } - unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[skippy_iter.idx].codepoint); + unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[idx].codepoint); if (base_index == NOT_COVERED) { - buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1); return_trace (false); } - return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx)); + return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, idx)); } bool subset (hb_subset_context_t *c) const diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPos.hh index 7e74aa73e0a..b10102880c0 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPos.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPos.hh @@ -11,18 +11,24 @@ struct MarkLigPos { protected: union { - HBUINT16 format; /* Format identifier */ - MarkLigPosFormat1 format1; + HBUINT16 format; /* Format identifier */ + MarkLigPosFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + MarkLigPosFormat1_2 format2; +#endif } u; public: template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); - if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif default:return_trace (c->default_return_value ()); } } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPosFormat1.hh index 4382aa6c6cb..4accd8166ba 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPosFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPosFormat1.hh @@ -1,72 +1,34 @@ #ifndef OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH #define OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH +#include "LigatureArray.hh" + namespace OT { namespace Layout { namespace GPOS_impl { -typedef AnchorMatrix LigatureAttach; /* component-major-- - * in order of writing direction--, - * mark-minor-- - * ordered by class--zero-based. */ -/* Array of LigatureAttach tables ordered by LigatureCoverage Index */ -struct LigatureArray : List16OfOffset16To -{ - template - bool subset (hb_subset_context_t *c, - Iterator coverage, - unsigned class_count, - const hb_map_t *klass_mapping) const - { - TRACE_SUBSET (this); - const hb_set_t &glyphset = *c->plan->glyphset_gsub (); - - auto *out = c->serializer->start_embed (this); - if (unlikely (!c->serializer->extend_min (out))) return_trace (false); - - for (const auto _ : + hb_zip (coverage, *this) - | hb_filter (glyphset, hb_first)) - { - auto *matrix = out->serialize_append (c->serializer); - if (unlikely (!matrix)) return_trace (false); - - const LigatureAttach& src = (this + _.second); - auto indexes = - + hb_range (src.rows * class_count) - | hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); }) - ; - matrix->serialize_subset (c, - _.second, - this, - src.rows, - indexes); - } - return_trace (this->len); - } -}; - -struct MarkLigPosFormat1 +template +struct MarkLigPosFormat1_2 { protected: HBUINT16 format; /* Format identifier--format = 1 */ - Offset16To + typename Types::template OffsetTo markCoverage; /* Offset to Mark Coverage table--from * beginning of MarkLigPos subtable */ - Offset16To + typename Types::template OffsetTo ligatureCoverage; /* Offset to Ligature Coverage * table--from beginning of MarkLigPos * subtable */ HBUINT16 classCount; /* Number of defined mark classes */ - Offset16To + typename Types::template OffsetTo markArray; /* Offset to MarkArray table--from * beginning of MarkLigPos subtable */ - Offset16To + typename Types::template OffsetTo ligatureArray; /* Offset to LigatureArray table--from * beginning of MarkLigPos subtable */ public: - DEFINE_SIZE_STATIC (12); + DEFINE_SIZE_STATIC (4 + 4 * Types::size); bool sanitize (hb_sanitize_context_t *c) const { @@ -138,20 +100,37 @@ struct MarkLigPosFormat1 if (likely (mark_index == NOT_COVERED)) return_trace (false); /* Now we search backwards for a non-mark glyph */ + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; - skippy_iter.reset (buffer->idx, 1); skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); - unsigned unsafe_from; - if (!skippy_iter.prev (&unsafe_from)) + + if (c->last_base_until > buffer->idx) + { + c->last_base_until = 0; + c->last_base = -1; + } + unsigned j; + for (j = buffer->idx; j > c->last_base_until; j--) { - buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1); + auto match = skippy_iter.match (buffer->info[j - 1]); + if (match == skippy_iter.MATCH) + { + c->last_base = (signed) j - 1; + break; + } + } + c->last_base_until = buffer->idx; + if (c->last_base == -1) + { + buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1); return_trace (false); } + j = (unsigned) c->last_base; + /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */ - //if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { return_trace (false); } + //if (!_hb_glyph_info_is_ligature (&buffer->info[j])) { return_trace (false); } - unsigned int j = skippy_iter.idx; unsigned int lig_index = (this+ligatureCoverage).get_coverage (buffer->info[j].codepoint); if (lig_index == NOT_COVERED) { diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPos.hh index c0eee6d54cf..e0d9eca0280 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPos.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPos.hh @@ -11,18 +11,24 @@ struct MarkMarkPos { protected: union { - HBUINT16 format; /* Format identifier */ - MarkMarkPosFormat1 format1; + HBUINT16 format; /* Format identifier */ + MarkMarkPosFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + MarkMarkPosFormat1_2 format2; +#endif } u; public: template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); - if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif default:return_trace (c->default_return_value ()); } } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh index c48a74f7738..fbcebb80441 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh @@ -12,27 +12,28 @@ typedef AnchorMatrix Mark2Array; /* mark2-major-- * mark1-minor-- * ordered by class--zero-based. */ -struct MarkMarkPosFormat1 +template +struct MarkMarkPosFormat1_2 { protected: HBUINT16 format; /* Format identifier--format = 1 */ - Offset16To + typename Types::template OffsetTo mark1Coverage; /* Offset to Combining Mark1 Coverage * table--from beginning of MarkMarkPos * subtable */ - Offset16To + typename Types::template OffsetTo mark2Coverage; /* Offset to Combining Mark2 Coverage * table--from beginning of MarkMarkPos * subtable */ HBUINT16 classCount; /* Number of defined mark classes */ - Offset16To + typename Types::template OffsetTo mark1Array; /* Offset to Mark1Array table--from * beginning of MarkMarkPos subtable */ - Offset16To + typename Types::template OffsetTo mark2Array; /* Offset to Mark2Array table--from * beginning of MarkMarkPos subtable */ public: - DEFINE_SIZE_STATIC (12); + DEFINE_SIZE_STATIC (4 + 4 * Types::size); bool sanitize (hb_sanitize_context_t *c) const { @@ -100,7 +101,7 @@ struct MarkMarkPosFormat1 /* now we search backwards for a suitable mark glyph until a non-mark glyph */ hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; skippy_iter.reset (buffer->idx, 1); - skippy_iter.set_lookup_props (c->lookup_props & ~LookupFlag::IgnoreFlags); + skippy_iter.set_lookup_props (c->lookup_props & ~(uint32_t)LookupFlag::IgnoreFlags); unsigned unsafe_from; if (!skippy_iter.prev (&unsafe_from)) { diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkRecord.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkRecord.hh index 7a514453aee..a7d489d2a51 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkRecord.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkRecord.hh @@ -9,7 +9,7 @@ struct MarkRecord { friend struct MarkArray; - protected: + public: HBUINT16 klass; /* Class defined for this mark */ Offset16To markAnchor; /* Offset to Anchor table--from diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPos.hh index 8479178d384..e3794ea9ed5 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPos.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPos.hh @@ -12,20 +12,28 @@ struct PairPos { protected: union { - HBUINT16 format; /* Format identifier */ - PairPosFormat1 format1; - PairPosFormat2 format2; + HBUINT16 format; /* Format identifier */ + PairPosFormat1_3 format1; + PairPosFormat2_4 format2; +#ifndef HB_NO_BEYOND_64K + PairPosFormat1_3 format3; + PairPosFormat2_4 format4; +#endif } u; public: template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); - if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...)); +#endif default:return_trace (c->default_return_value ()); } } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh index 35a2db2d45e..ec9475d113f 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh @@ -1,248 +1,22 @@ #ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH #define OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH +#include "PairSet.hh" + namespace OT { namespace Layout { namespace GPOS_impl { -struct PairValueRecord -{ - friend struct PairSet; - - int cmp (hb_codepoint_t k) const - { return secondGlyph.cmp (k); } - - struct context_t - { - const void *base; - const ValueFormat *valueFormats; - const ValueFormat *newFormats; - unsigned len1; /* valueFormats[0].get_len() */ - const hb_map_t *glyph_map; - const hb_map_t *layout_variation_idx_map; - }; - - bool subset (hb_subset_context_t *c, - context_t *closure) const - { - TRACE_SERIALIZE (this); - auto *s = c->serializer; - auto *out = s->start_embed (*this); - if (unlikely (!s->extend_min (out))) return_trace (false); - - out->secondGlyph = (*closure->glyph_map)[secondGlyph]; - - closure->valueFormats[0].copy_values (s, - closure->newFormats[0], - closure->base, &values[0], - closure->layout_variation_idx_map); - closure->valueFormats[1].copy_values (s, - closure->newFormats[1], - closure->base, - &values[closure->len1], - closure->layout_variation_idx_map); - - return_trace (true); - } - - void collect_variation_indices (hb_collect_variation_indices_context_t *c, - const ValueFormat *valueFormats, - const void *base) const - { - unsigned record1_len = valueFormats[0].get_len (); - unsigned record2_len = valueFormats[1].get_len (); - const hb_array_t values_array = values.as_array (record1_len + record2_len); - - if (valueFormats[0].has_device ()) - valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len)); - - if (valueFormats[1].has_device ()) - valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len)); - } - - bool intersects (const hb_set_t& glyphset) const - { - return glyphset.has(secondGlyph); - } - - const Value* get_values_1 () const - { - return &values[0]; - } - - const Value* get_values_2 (ValueFormat format1) const - { - return &values[format1.get_len ()]; - } - - protected: - HBGlyphID16 secondGlyph; /* GlyphID of second glyph in the - * pair--first glyph is listed in the - * Coverage table */ - ValueRecord values; /* Positioning data for the first glyph - * followed by for second glyph */ - public: - DEFINE_SIZE_ARRAY (2, values); -}; -struct PairSet +template +struct PairPosFormat1_3 { - friend struct PairPosFormat1; - - bool intersects (const hb_set_t *glyphs, - const ValueFormat *valueFormats) const - { - unsigned int len1 = valueFormats[0].get_len (); - unsigned int len2 = valueFormats[1].get_len (); - unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2); - - const PairValueRecord *record = &firstPairValueRecord; - unsigned int count = len; - for (unsigned int i = 0; i < count; i++) - { - if (glyphs->has (record->secondGlyph)) - return true; - record = &StructAtOffset (record, record_size); - } - return false; - } - - void collect_glyphs (hb_collect_glyphs_context_t *c, - const ValueFormat *valueFormats) const - { - unsigned int len1 = valueFormats[0].get_len (); - unsigned int len2 = valueFormats[1].get_len (); - unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2); - - const PairValueRecord *record = &firstPairValueRecord; - c->input->add_array (&record->secondGlyph, len, record_size); - } - - void collect_variation_indices (hb_collect_variation_indices_context_t *c, - const ValueFormat *valueFormats) const - { - unsigned len1 = valueFormats[0].get_len (); - unsigned len2 = valueFormats[1].get_len (); - unsigned record_size = HBUINT16::static_size * (1 + len1 + len2); - - const PairValueRecord *record = &firstPairValueRecord; - unsigned count = len; - for (unsigned i = 0; i < count; i++) - { - if (c->glyph_set->has (record->secondGlyph)) - { record->collect_variation_indices (c, valueFormats, this); } - - record = &StructAtOffset (record, record_size); - } - } - - bool apply (hb_ot_apply_context_t *c, - const ValueFormat *valueFormats, - unsigned int pos) const - { - TRACE_APPLY (this); - hb_buffer_t *buffer = c->buffer; - unsigned int len1 = valueFormats[0].get_len (); - unsigned int len2 = valueFormats[1].get_len (); - unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2); - - const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint, - &firstPairValueRecord, - len, - record_size); - if (record) - { - bool applied_first = valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()); - bool applied_second = valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]); - if (applied_first || applied_second) - buffer->unsafe_to_break (buffer->idx, pos + 1); - if (len2) - pos++; - buffer->idx = pos; - return_trace (true); - } - buffer->unsafe_to_concat (buffer->idx, pos + 1); - return_trace (false); - } - - bool subset (hb_subset_context_t *c, - const ValueFormat valueFormats[2], - const ValueFormat newFormats[2]) const - { - TRACE_SUBSET (this); - auto snap = c->serializer->snapshot (); - - auto *out = c->serializer->start_embed (*this); - if (unlikely (!c->serializer->extend_min (out))) return_trace (false); - out->len = 0; - - const hb_set_t &glyphset = *c->plan->glyphset_gsub (); - const hb_map_t &glyph_map = *c->plan->glyph_map; - - unsigned len1 = valueFormats[0].get_len (); - unsigned len2 = valueFormats[1].get_len (); - unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2); - - PairValueRecord::context_t context = - { - this, - valueFormats, - newFormats, - len1, - &glyph_map, - c->plan->layout_variation_idx_map - }; - - const PairValueRecord *record = &firstPairValueRecord; - unsigned count = len, num = 0; - for (unsigned i = 0; i < count; i++) - { - if (glyphset.has (record->secondGlyph) - && record->subset (c, &context)) num++; - record = &StructAtOffset (record, record_size); - } - - out->len = num; - if (!num) c->serializer->revert (snap); - return_trace (num); - } - - struct sanitize_closure_t - { - const ValueFormat *valueFormats; - unsigned int len1; /* valueFormats[0].get_len() */ - unsigned int stride; /* 1 + len1 + len2 */ - }; - - bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const - { - TRACE_SANITIZE (this); - if (!(c->check_struct (this) - && c->check_range (&firstPairValueRecord, - len, - HBUINT16::static_size, - closure->stride))) return_trace (false); - - unsigned int count = len; - const PairValueRecord *record = &firstPairValueRecord; - return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) && - closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride)); - } + using PairSet = GPOS_impl::PairSet; + using PairValueRecord = GPOS_impl::PairValueRecord; - protected: - HBUINT16 len; /* Number of PairValueRecords */ - PairValueRecord firstPairValueRecord; - /* Array of PairValueRecords--ordered - * by GlyphID of the second glyph */ - public: - DEFINE_SIZE_MIN (2); -}; - -struct PairPosFormat1 -{ protected: HBUINT16 format; /* Format identifier--format = 1 */ - Offset16To + typename Types::template OffsetTo coverage; /* Offset to Coverage table--from * beginning of subtable */ ValueFormat valueFormat[2]; /* [0] Defines the types of data in @@ -251,11 +25,11 @@ struct PairPosFormat1 /* [1] Defines the types of data in * ValueRecord2--for the second glyph * in the pair--may be zero (0) */ - Array16OfOffset16To + Array16Of> pairSet; /* Array of PairSet tables * ordered by Coverage Index */ public: - DEFINE_SIZE_ARRAY (10, pairSet); + DEFINE_SIZE_ARRAY (8 + Types::size, pairSet); bool sanitize (hb_sanitize_context_t *c) const { @@ -265,24 +39,36 @@ struct PairPosFormat1 unsigned int len1 = valueFormat[0].get_len (); unsigned int len2 = valueFormat[1].get_len (); - PairSet::sanitize_closure_t closure = + typename PairSet::sanitize_closure_t closure = { valueFormat, len1, - 1 + len1 + len2 + PairSet::get_size (len1, len2) }; return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure)); } - bool intersects (const hb_set_t *glyphs) const { + auto &cov = this+coverage; + + if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len) / 4) + { + for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);) + { + unsigned i = cov.get_coverage (g); + if ((this+pairSet[i]).intersects (glyphs, valueFormat)) + return true; + } + return false; + } + return - + hb_zip (this+coverage, pairSet) + + hb_zip (cov, pairSet) | hb_filter (*glyphs, hb_first) | hb_map (hb_second) - | hb_map ([glyphs, this] (const Offset16To &_) + | hb_map ([glyphs, this] (const typename Types::template OffsetTo &_) { return (this+_).intersects (glyphs, valueFormat); }) | hb_any ; @@ -354,11 +140,17 @@ struct PairPosFormat1 out->valueFormat[1] = newFormats.second; } + if (c->plan->all_axes_pinned) + { + out->valueFormat[0] = out->valueFormat[0].drop_device_table_flags (); + out->valueFormat[1] = out->valueFormat[1].drop_device_table_flags (); + } + hb_sorted_vector_t new_coverage; + hb_zip (this+coverage, pairSet) | hb_filter (glyphset, hb_first) - | hb_filter ([this, c, out] (const Offset16To& _) + | hb_filter ([this, c, out] (const typename Types::template OffsetTo& _) { auto snap = c->serializer->snapshot (); auto *o = out->pairSet.serialize_append (c->serializer); @@ -385,19 +177,21 @@ struct PairPosFormat1 hb_pair_t compute_effective_value_formats (const hb_set_t& glyphset) const { - unsigned len1 = valueFormat[0].get_len (); - unsigned len2 = valueFormat[1].get_len (); - unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2); + unsigned record_size = PairSet::get_size (valueFormat); unsigned format1 = 0; unsigned format2 = 0; - for (const Offset16To& _ : - + hb_zip (this+coverage, pairSet) | hb_filter (glyphset, hb_first) | hb_map (hb_second)) + for (const auto & _ : + + hb_zip (this+coverage, pairSet) + | hb_filter (glyphset, hb_first) + | hb_map (hb_second) + ) { const PairSet& set = (this + _); const PairValueRecord *record = &set.firstPairValueRecord; - for (unsigned i = 0; i < set.len; i++) + unsigned count = set.len; + for (unsigned i = 0; i < count; i++) { if (record->intersects (glyphset)) { @@ -406,6 +200,9 @@ struct PairPosFormat1 } record = &StructAtOffset (record, record_size); } + + if (format1 == valueFormat[0] && format2 == valueFormat[1]) + break; } return hb_pair (format1, format2); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh index 3f5f9959c46..17486dddaf7 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh @@ -7,11 +7,12 @@ namespace OT { namespace Layout { namespace GPOS_impl { -struct PairPosFormat2 +template +struct PairPosFormat2_4 { protected: HBUINT16 format; /* Format identifier--format = 2 */ - Offset16To + typename Types::template OffsetTo coverage; /* Offset to Coverage table--from * beginning of subtable */ ValueFormat valueFormat1; /* ValueRecord definition--for the @@ -20,11 +21,11 @@ struct PairPosFormat2 ValueFormat valueFormat2; /* ValueRecord definition--for the * second glyph of the pair--may be * zero (0) */ - Offset16To + typename Types::template OffsetTo classDef1; /* Offset to ClassDef table--from * beginning of PairPos subtable--for * the first glyph of the pair */ - Offset16To + typename Types::template OffsetTo classDef2; /* Offset to ClassDef table--from * beginning of PairPos subtable--for * the second glyph of the pair */ @@ -36,7 +37,7 @@ struct PairPosFormat2 * class1-major, class2-minor, * Each entry has value1 and value2 */ public: - DEFINE_SIZE_ARRAY (16, values); + DEFINE_SIZE_ARRAY (10 + 3 * Types::size, values); bool sanitize (hb_sanitize_context_t *c) const { @@ -48,7 +49,7 @@ struct PairPosFormat2 unsigned int len1 = valueFormat1.get_len (); unsigned int len2 = valueFormat2.get_len (); - unsigned int stride = len1 + len2; + unsigned int stride = HBUINT16::static_size * (len1 + len2); unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size (); unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count; return_trace (c->check_range ((const void *) values, @@ -216,10 +217,31 @@ struct PairPosFormat2 } bail: + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "try kerning glyphs at %u,%u", + c->buffer->idx, skippy_iter.idx); + } applied_first = valueFormat1.apply_value (c, this, v, buffer->cur_pos()); applied_second = valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]); + if (applied_first || applied_second) + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "kerned glyphs at %u,%u", + c->buffer->idx, skippy_iter.idx); + } + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "tried kerning glyphs at %u,%u", + c->buffer->idx, skippy_iter.idx); + } + success: if (applied_first || applied_second) buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1); @@ -227,10 +249,15 @@ struct PairPosFormat2 boring: buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1); + if (len2) + { + skippy_iter.idx++; + // https://github.com/harfbuzz/harfbuzz/issues/3824 + // https://github.com/harfbuzz/harfbuzz/issues/3888#issuecomment-1326781116 + buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1); + } buffer->idx = skippy_iter.idx; - if (len2) - buffer->idx++; return_trace (true); } @@ -260,13 +287,19 @@ struct PairPosFormat2 out->valueFormat1 = newFormats.first; out->valueFormat2 = newFormats.second; + if (c->plan->all_axes_pinned) + { + out->valueFormat1 = out->valueFormat1.drop_device_table_flags (); + out->valueFormat2 = out->valueFormat2.drop_device_table_flags (); + } + for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map)) { for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map)) { unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); - valueFormat1.copy_values (c->serializer, newFormats.first, this, &values[idx], c->plan->layout_variation_idx_map); - valueFormat2.copy_values (c->serializer, newFormats.second, this, &values[idx + len1], c->plan->layout_variation_idx_map); + valueFormat1.copy_values (c->serializer, out->valueFormat1, this, &values[idx], &c->plan->layout_variation_idx_delta_map); + valueFormat2.copy_values (c->serializer, out->valueFormat2, this, &values[idx + len1], &c->plan->layout_variation_idx_delta_map); } } @@ -289,6 +322,7 @@ struct PairPosFormat2 { unsigned len1 = valueFormat1.get_len (); unsigned len2 = valueFormat2.get_len (); + unsigned record_size = len1 + len2; unsigned format1 = 0; unsigned format2 = 0; @@ -297,10 +331,13 @@ struct PairPosFormat2 { for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map)) { - unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); + unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * record_size; format1 = format1 | valueFormat1.get_effective_format (&values[idx]); format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1]); } + + if (format1 == valueFormat1 && format2 == valueFormat2) + break; } return hb_pair (format1, format2); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairSet.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairSet.hh new file mode 100644 index 00000000000..adeb08e910b --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairSet.hh @@ -0,0 +1,207 @@ +#ifndef OT_LAYOUT_GPOS_PAIRSET_HH +#define OT_LAYOUT_GPOS_PAIRSET_HH + +#include "PairValueRecord.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template +struct PairSet +{ + template + friend struct PairPosFormat1_3; + + using PairValueRecord = GPOS_impl::PairValueRecord; + + protected: + HBUINT16 len; /* Number of PairValueRecords */ + PairValueRecord firstPairValueRecord; + /* Array of PairValueRecords--ordered + * by GlyphID of the second glyph */ + public: + DEFINE_SIZE_MIN (2); + + static unsigned get_size (unsigned len1, unsigned len2) + { + return Types::HBGlyphID::static_size + Value::static_size * (len1 + len2); + } + static unsigned get_size (const ValueFormat valueFormats[2]) + { + unsigned len1 = valueFormats[0].get_len (); + unsigned len2 = valueFormats[1].get_len (); + return get_size (len1, len2); + } + + struct sanitize_closure_t + { + const ValueFormat *valueFormats; + unsigned int len1; /* valueFormats[0].get_len() */ + unsigned int stride; /* bytes */ + }; + + bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const + { + TRACE_SANITIZE (this); + if (!(c->check_struct (this) + && c->check_range (&firstPairValueRecord, + len, + closure->stride))) return_trace (false); + + unsigned int count = len; + const PairValueRecord *record = &firstPairValueRecord; + return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) && + closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride)); + } + + bool intersects (const hb_set_t *glyphs, + const ValueFormat *valueFormats) const + { + unsigned record_size = get_size (valueFormats); + + const PairValueRecord *record = &firstPairValueRecord; + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + { + if (glyphs->has (record->secondGlyph)) + return true; + record = &StructAtOffset (record, record_size); + } + return false; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c, + const ValueFormat *valueFormats) const + { + unsigned record_size = get_size (valueFormats); + + const PairValueRecord *record = &firstPairValueRecord; + c->input->add_array (&record->secondGlyph, len, record_size); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const ValueFormat *valueFormats) const + { + unsigned record_size = get_size (valueFormats); + + const PairValueRecord *record = &firstPairValueRecord; + unsigned count = len; + for (unsigned i = 0; i < count; i++) + { + if (c->glyph_set->has (record->secondGlyph)) + { record->collect_variation_indices (c, valueFormats, this); } + + record = &StructAtOffset (record, record_size); + } + } + + bool apply (hb_ot_apply_context_t *c, + const ValueFormat *valueFormats, + unsigned int pos) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int len1 = valueFormats[0].get_len (); + unsigned int len2 = valueFormats[1].get_len (); + unsigned record_size = get_size (len1, len2); + + const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint, + &firstPairValueRecord, + len, + record_size); + if (record) + { + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "try kerning glyphs at %u,%u", + c->buffer->idx, pos); + } + + bool applied_first = valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()); + bool applied_second = valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]); + + if (applied_first || applied_second) + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "kerned glyphs at %u,%u", + c->buffer->idx, pos); + } + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "tried kerning glyphs at %u,%u", + c->buffer->idx, pos); + } + + if (applied_first || applied_second) + buffer->unsafe_to_break (buffer->idx, pos + 1); + + if (len2) + { + pos++; + // https://github.com/harfbuzz/harfbuzz/issues/3824 + // https://github.com/harfbuzz/harfbuzz/issues/3888#issuecomment-1326781116 + buffer->unsafe_to_break (buffer->idx, pos + 1); + } + + buffer->idx = pos; + return_trace (true); + } + buffer->unsafe_to_concat (buffer->idx, pos + 1); + return_trace (false); + } + + bool subset (hb_subset_context_t *c, + const ValueFormat valueFormats[2], + const ValueFormat newFormats[2]) const + { + TRACE_SUBSET (this); + auto snap = c->serializer->snapshot (); + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->len = 0; + + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + unsigned len1 = valueFormats[0].get_len (); + unsigned len2 = valueFormats[1].get_len (); + unsigned record_size = get_size (len1, len2); + + typename PairValueRecord::context_t context = + { + this, + valueFormats, + newFormats, + len1, + &glyph_map, + &c->plan->layout_variation_idx_delta_map + }; + + const PairValueRecord *record = &firstPairValueRecord; + unsigned count = len, num = 0; + for (unsigned i = 0; i < count; i++) + { + if (glyphset.has (record->secondGlyph) + && record->subset (c, &context)) num++; + record = &StructAtOffset (record, record_size); + } + + out->len = num; + if (!num) c->serializer->revert (snap); + return_trace (num); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRSET_HH diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairValueRecord.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairValueRecord.hh new file mode 100644 index 00000000000..d4f549a480d --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairValueRecord.hh @@ -0,0 +1,99 @@ +#ifndef OT_LAYOUT_GPOS_PAIRVALUERECORD_HH +#define OT_LAYOUT_GPOS_PAIRVALUERECORD_HH + +#include "ValueFormat.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template +struct PairValueRecord +{ + template + friend struct PairSet; + + protected: + typename Types::HBGlyphID + secondGlyph; /* GlyphID of second glyph in the + * pair--first glyph is listed in the + * Coverage table */ + ValueRecord values; /* Positioning data for the first glyph + * followed by for second glyph */ + public: + DEFINE_SIZE_ARRAY (Types::size, values); + + int cmp (hb_codepoint_t k) const + { return secondGlyph.cmp (k); } + + struct context_t + { + const void *base; + const ValueFormat *valueFormats; + const ValueFormat *newFormats; + unsigned len1; /* valueFormats[0].get_len() */ + const hb_map_t *glyph_map; + const hb_hashmap_t> *layout_variation_idx_delta_map; + }; + + bool subset (hb_subset_context_t *c, + context_t *closure) const + { + TRACE_SERIALIZE (this); + auto *s = c->serializer; + auto *out = s->start_embed (*this); + if (unlikely (!s->extend_min (out))) return_trace (false); + + out->secondGlyph = (*closure->glyph_map)[secondGlyph]; + + closure->valueFormats[0].copy_values (s, + closure->newFormats[0], + closure->base, &values[0], + closure->layout_variation_idx_delta_map); + closure->valueFormats[1].copy_values (s, + closure->newFormats[1], + closure->base, + &values[closure->len1], + closure->layout_variation_idx_delta_map); + + return_trace (true); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const ValueFormat *valueFormats, + const void *base) const + { + unsigned record1_len = valueFormats[0].get_len (); + unsigned record2_len = valueFormats[1].get_len (); + const hb_array_t values_array = values.as_array (record1_len + record2_len); + + if (valueFormats[0].has_device ()) + valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len)); + + if (valueFormats[1].has_device ()) + valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len)); + } + + bool intersects (const hb_set_t& glyphset) const + { + return glyphset.has(secondGlyph); + } + + const Value* get_values_1 () const + { + return &values[0]; + } + + const Value* get_values_2 (ValueFormat format1) const + { + return &values[format1.get_len ()]; + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRVALUERECORD_HH diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh index 57e146befd6..3af6c499659 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh @@ -38,17 +38,18 @@ struct SinglePos void serialize (hb_serialize_context_t *c, const SrcLookup* src, Iterator glyph_val_iter_pairs, - const hb_map_t *layout_variation_idx_map) + const hb_hashmap_t> *layout_variation_idx_delta_map, + bool all_axes_pinned) { if (unlikely (!c->extend_min (u.format))) return; unsigned format = 2; ValueFormat new_format = src->get_value_format (); + if (all_axes_pinned) + new_format = new_format.drop_device_table_flags (); + if (glyph_val_iter_pairs) - { format = get_format (glyph_val_iter_pairs); - new_format = src->get_value_format ().get_effective_format (+ glyph_val_iter_pairs | hb_map (hb_second)); - } u.format = format; switch (u.format) { @@ -56,13 +57,13 @@ struct SinglePos src, glyph_val_iter_pairs, new_format, - layout_variation_idx_map); + layout_variation_idx_delta_map); return; case 2: u.format2.serialize (c, src, glyph_val_iter_pairs, new_format, - layout_variation_idx_map); + layout_variation_idx_delta_map); return; default:return; } @@ -71,8 +72,8 @@ struct SinglePos template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); - if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); @@ -87,8 +88,9 @@ static void SinglePos_serialize (hb_serialize_context_t *c, const SrcLookup *src, Iterator it, - const hb_map_t *layout_variation_idx_map) -{ c->start_embed ()->serialize (c, src, it, layout_variation_idx_map); } + const hb_hashmap_t> *layout_variation_idx_delta_map, + bool all_axes_pinned) +{ c->start_embed ()->serialize (c, src, it, layout_variation_idx_delta_map, all_axes_pinned); } } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat1.hh index 8b7840ed0eb..53aff9f3dfc 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat1.hh @@ -39,12 +39,10 @@ struct SinglePosFormat1 { if (!valueFormat.has_device ()) return; - auto it = - + hb_iter (this+coverage) - | hb_filter (c->glyph_set) - ; + hb_set_t intersection; + (this+coverage).intersect_set (*c->glyph_set, intersection); + if (!intersection) return; - if (!it) return; valueFormat.collect_variation_indices (c, this, values.as_array (valueFormat.get_len ())); } @@ -62,12 +60,44 @@ struct SinglePosFormat1 unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); if (likely (index == NOT_COVERED)) return_trace (false); + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioning glyph at %u", + c->buffer->idx); + } + valueFormat.apply_value (c, this, values, buffer->cur_pos()); + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioned glyph at %u", + c->buffer->idx); + } + buffer->idx++; return_trace (true); } + bool + position_single (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t gid, + hb_glyph_position_t &pos) const + { + unsigned int index = (this+coverage).get_coverage (gid); + if (likely (index == NOT_COVERED)) return false; + + /* This is ugly... */ + hb_buffer_t buffer; + buffer.props.direction = direction; + OT::hb_ot_apply_context_t c (1, font, &buffer); + + valueFormat.apply_value (&c, this, values, pos); + return true; + } + template @@ -75,7 +105,7 @@ struct SinglePosFormat1 const SrcLookup *src, Iterator it, ValueFormat newFormat, - const hb_map_t *layout_variation_idx_map) + const hb_hashmap_t> *layout_variation_idx_delta_map) { if (unlikely (!c->extend_min (this))) return; if (unlikely (!c->check_assign (valueFormat, @@ -84,7 +114,7 @@ struct SinglePosFormat1 for (const hb_array_t& _ : + it | hb_map (hb_second)) { - src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_map); + src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_delta_map); // Only serialize the first entry in the iterator, the rest are assumed to // be the same. break; @@ -104,15 +134,17 @@ struct SinglePosFormat1 const hb_set_t &glyphset = *c->plan->glyphset_gsub (); const hb_map_t &glyph_map = *c->plan->glyph_map; + hb_set_t intersection; + (this+coverage).intersect_set (glyphset, intersection); + auto it = - + hb_iter (this+coverage) - | hb_filter (glyphset) + + hb_iter (intersection) | hb_map_retains_sorting (glyph_map) | hb_zip (hb_repeat (values.as_array (valueFormat.get_len ()))) ; bool ret = bool (it); - SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_map); + SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned); return_trace (ret); } }; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat2.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat2.hh index 0d038b44220..6546eb16703 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat2.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePosFormat2.hh @@ -68,16 +68,52 @@ struct SinglePosFormat2 unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); if (likely (index == NOT_COVERED)) return_trace (false); - if (likely (index >= valueCount)) return_trace (false); + if (unlikely (index >= valueCount)) return_trace (false); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioning glyph at %u", + c->buffer->idx); + } valueFormat.apply_value (c, this, &values[index * valueFormat.get_len ()], buffer->cur_pos()); + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioned glyph at %u", + c->buffer->idx); + } + buffer->idx++; return_trace (true); } + bool + position_single (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t gid, + hb_glyph_position_t &pos) const + { + unsigned int index = (this+coverage).get_coverage (gid); + if (likely (index == NOT_COVERED)) return false; + if (unlikely (index >= valueCount)) return false; + + /* This is ugly... */ + hb_buffer_t buffer; + buffer.props.direction = direction; + OT::hb_ot_apply_context_t c (1, font, &buffer); + + valueFormat.apply_value (&c, this, + &values[index * valueFormat.get_len ()], + pos); + return true; + } + + template @@ -85,7 +121,7 @@ struct SinglePosFormat2 const SrcLookup *src, Iterator it, ValueFormat newFormat, - const hb_map_t *layout_variation_idx_map) + const hb_hashmap_t> *layout_variation_idx_delta_map) { auto out = c->extend_min (this); if (unlikely (!out)) return; @@ -95,7 +131,7 @@ struct SinglePosFormat2 + it | hb_map (hb_second) | hb_apply ([&] (hb_array_t _) - { src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_map); }) + { src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_delta_map); }) ; auto glyphs = @@ -127,7 +163,7 @@ struct SinglePosFormat2 ; bool ret = bool (it); - SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_map); + SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned); return_trace (ret); } }; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh index b29f287bcee..1aa451abcc8 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh @@ -59,6 +59,24 @@ struct ValueFormat : HBUINT16 unsigned int get_len () const { return hb_popcount ((unsigned int) *this); } unsigned int get_size () const { return get_len () * Value::static_size; } + hb_vector_t get_device_table_indices () const { + unsigned i = 0; + hb_vector_t result; + unsigned format = *this; + + if (format & xPlacement) i++; + if (format & yPlacement) i++; + if (format & xAdvance) i++; + if (format & yAdvance) i++; + + if (format & xPlaDevice) result.push (i++); + if (format & yPlaDevice) result.push (i++); + if (format & xAdvDevice) result.push (i++); + if (format & yAdvDevice) result.push (i++); + + return result; + } + bool apply_value (hb_ot_apply_context_t *c, const void *base, const Value *values, @@ -145,30 +163,50 @@ struct ValueFormat : HBUINT16 unsigned int new_format, const void *base, const Value *values, - const hb_map_t *layout_variation_idx_map) const + const hb_hashmap_t> *layout_variation_idx_delta_map) const { unsigned int format = *this; if (!format) return; - if (format & xPlacement) copy_value (c, new_format, xPlacement, *values++); - if (format & yPlacement) copy_value (c, new_format, yPlacement, *values++); - if (format & xAdvance) copy_value (c, new_format, xAdvance, *values++); - if (format & yAdvance) copy_value (c, new_format, yAdvance, *values++); + HBINT16 *x_placement = nullptr, *y_placement = nullptr, *x_adv = nullptr, *y_adv = nullptr; + if (format & xPlacement) x_placement = copy_value (c, new_format, xPlacement, *values++); + if (format & yPlacement) y_placement = copy_value (c, new_format, yPlacement, *values++); + if (format & xAdvance) x_adv = copy_value (c, new_format, xAdvance, *values++); + if (format & yAdvance) y_adv = copy_value (c, new_format, yAdvance, *values++); - if (format & xPlaDevice) copy_device (c, base, values++, layout_variation_idx_map); - if (format & yPlaDevice) copy_device (c, base, values++, layout_variation_idx_map); - if (format & xAdvDevice) copy_device (c, base, values++, layout_variation_idx_map); - if (format & yAdvDevice) copy_device (c, base, values++, layout_variation_idx_map); + if (format & xPlaDevice) + { + add_delta_to_value (x_placement, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xPlaDevice); + } + + if (format & yPlaDevice) + { + add_delta_to_value (y_placement, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yPlaDevice); + } + + if (format & xAdvDevice) + { + add_delta_to_value (x_adv, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xAdvDevice); + } + + if (format & yAdvDevice) + { + add_delta_to_value (y_adv, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yAdvDevice); + } } - void copy_value (hb_serialize_context_t *c, - unsigned int new_format, - Flags flag, - Value value) const + HBINT16* copy_value (hb_serialize_context_t *c, + unsigned int new_format, + Flags flag, + Value value) const { // Filter by new format. - if (!(new_format & flag)) return; - c->copy (value); + if (!(new_format & flag)) return nullptr; + return reinterpret_cast (c->copy (value)); } void collect_variation_indices (hb_collect_variation_indices_context_t *c, @@ -183,31 +221,40 @@ struct ValueFormat : HBUINT16 if (format & yAdvance) i++; if (format & xPlaDevice) { - (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + (base + get_device (&(values[i]))).collect_variation_indices (c); i++; } if (format & ValueFormat::yPlaDevice) { - (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + (base + get_device (&(values[i]))).collect_variation_indices (c); i++; } if (format & ValueFormat::xAdvDevice) { - (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + (base + get_device (&(values[i]))).collect_variation_indices (c); i++; } if (format & ValueFormat::yAdvDevice) { - (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + (base + get_device (&(values[i]))).collect_variation_indices (c); i++; } } + unsigned drop_device_table_flags () const + { + unsigned format = *this; + for (unsigned flag = xPlaDevice; flag <= yAdvDevice; flag = flag << 1) + format = format & ~flag; + + return format; + } + private: bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const { @@ -236,9 +283,27 @@ struct ValueFormat : HBUINT16 return *static_cast *> (value); } + void add_delta_to_value (HBINT16 *value, + const void *base, + const Value *src_value, + const hb_hashmap_t> *layout_variation_idx_delta_map) const + { + if (!value) return; + unsigned varidx = (base + get_device (src_value)).get_variation_index (); + hb_pair_t *varidx_delta; + if (!layout_variation_idx_delta_map->has (varidx, &varidx_delta)) return; + + *value += hb_second (*varidx_delta); + } + bool copy_device (hb_serialize_context_t *c, const void *base, - const Value *src_value, const hb_map_t *layout_variation_idx_map) const + const Value *src_value, + const hb_hashmap_t> *layout_variation_idx_delta_map, + unsigned int new_format, Flags flag) const { + // Filter by new format. + if (!(new_format & flag)) return true; + Value *dst_value = c->copy (*src_value); if (!dst_value) return false; @@ -246,7 +311,7 @@ struct ValueFormat : HBUINT16 *dst_value = 0; c->push (); - if ((base + get_device (src_value)).copy (c, layout_variation_idx_map)) + if ((base + get_device (src_value)).copy (c, layout_variation_idx_delta_map)) { c->add_link (*dst_value, c->pop_pack ()); return true; @@ -306,7 +371,7 @@ struct ValueFormat : HBUINT16 for (unsigned int i = 0; i < count; i++) { if (!sanitize_value_devices (c, base, values)) return_trace (false); - values += stride; + values = &StructAtOffset (values, stride); } return_trace (true); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSet.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSet.hh index 484f3474686..b5d506f36f9 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSet.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSet.hh @@ -5,12 +5,13 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { +template struct AlternateSet { protected: - Array16Of + Array16Of alternates; /* Array of alternate GlyphIDs--in * arbitrary order */ public: @@ -56,8 +57,23 @@ struct AlternateSet if (unlikely (alt_index > count || alt_index == 0)) return_trace (false); + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %u (alternate substitution)", + c->buffer->idx); + } + c->replace_glyph (alternates[alt_index - 1]); + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (alternate substitution)", + c->buffer->idx - 1u); + } + return_trace (true); } @@ -68,7 +84,7 @@ struct AlternateSet { if (alternates.len && alternate_count) { - + alternates.sub_array (start_offset, alternate_count) + + alternates.as_array ().sub_array (start_offset, alternate_count) | hb_sink (hb_array (alternate_glyphs, *alternate_count)) ; } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubst.hh index e5d999261fc..8951f5a7a17 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubst.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubst.hh @@ -6,28 +6,36 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { struct AlternateSubst { protected: union { - HBUINT16 format; /* Format identifier */ - AlternateSubstFormat1 format1; + HBUINT16 format; /* Format identifier */ + AlternateSubstFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + AlternateSubstFormat1_2 format2; +#endif } u; public: template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); - if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif default:return_trace (c->default_return_value ()); } } + /* TODO This function is unused and not updated to 24bit GIDs. Should be done by using + * iterators. While at it perhaps using iterator of arrays of hb_codepoint_t instead. */ bool serialize (hb_serialize_context_t *c, hb_sorted_array_t glyphs, hb_array_t alternate_len_list, @@ -42,6 +50,9 @@ struct AlternateSubst default:return_trace (false); } } + + /* TODO subset() should choose format. */ + }; } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh index af1cd7bedba..adec65d5864 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh @@ -6,20 +6,21 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { -struct AlternateSubstFormat1 +template +struct AlternateSubstFormat1_2 { protected: HBUINT16 format; /* Format identifier--format = 1 */ - Offset16To + typename Types::template OffsetTo coverage; /* Offset to Coverage table--from * beginning of Substitution table */ - Array16OfOffset16To + Array16Of>> alternateSet; /* Array of AlternateSet tables * ordered by Coverage Index */ public: - DEFINE_SIZE_ARRAY (6, alternateSet); + DEFINE_SIZE_ARRAY (2 + 2 * Types::size, alternateSet); bool sanitize (hb_sanitize_context_t *c) const { @@ -39,9 +40,8 @@ struct AlternateSubstFormat1 | hb_filter (c->parent_active_glyphs (), hb_first) | hb_map (hb_second) | hb_map (hb_add (this)) - | hb_apply ([c] (const AlternateSet &_) { _.closure (c); }) + | hb_apply ([c] (const AlternateSet &_) { _.closure (c); }) ; - } void closure_lookups (hb_closure_lookups_context_t *c) const {} @@ -52,7 +52,7 @@ struct AlternateSubstFormat1 + hb_zip (this+coverage, alternateSet) | hb_map (hb_second) | hb_map (hb_add (this)) - | hb_apply ([c] (const AlternateSet &_) { _.collect_glyphs (c); }) + | hb_apply ([c] (const AlternateSet &_) { _.collect_glyphs (c); }) ; } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ChainContextSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ChainContextSubst.hh index bbb88b222f8..08fd779f730 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ChainContextSubst.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ChainContextSubst.hh @@ -7,7 +7,7 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { struct ChainContextSubst : ChainContext {}; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Common.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Common.hh index f4c78a9f020..968bba0481a 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Common.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Common.hh @@ -6,7 +6,7 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { typedef hb_pair_t hb_codepoint_pair_t; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ContextSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ContextSubst.hh index 2af54e8ff47..9f8cb46b5e2 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ContextSubst.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ContextSubst.hh @@ -7,7 +7,7 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { struct ContextSubst : Context {}; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ExtensionSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ExtensionSubst.hh index 40a3ff439f1..831a7dfa2d1 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ExtensionSubst.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ExtensionSubst.hh @@ -7,7 +7,7 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { struct ExtensionSubst : Extension { diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/GSUB.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/GSUB.hh index 3f0c4b2ad9a..900cf603e45 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/GSUB.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/GSUB.hh @@ -1,16 +1,15 @@ #ifndef OT_LAYOUT_GSUB_GSUB_HH #define OT_LAYOUT_GSUB_GSUB_HH -// TODO(garretrieger): move to new layout. #include "../../../hb-ot-layout-gsubgpos.hh" #include "Common.hh" #include "SubstLookup.hh" -using OT::Layout::GSUB::SubstLookup; - namespace OT { + +using Layout::GSUB_impl::SubstLookup; + namespace Layout { -namespace GSUB { /* * GSUB -- Glyph Substitution @@ -28,12 +27,15 @@ struct GSUB : GSUBGPOS bool subset (hb_subset_context_t *c) const { - hb_subset_layout_context_t l (c, tableTag, c->plan->gsub_lookups, c->plan->gsub_langsys, c->plan->gsub_features); + hb_subset_layout_context_t l (c, tableTag); return GSUBGPOS::subset (&l); } bool sanitize (hb_sanitize_context_t *c) const - { return GSUBGPOS::sanitize (c); } + { + TRACE_SANITIZE (this); + return_trace (GSUBGPOS::sanitize (c)); + } HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, hb_face_t *face) const; @@ -47,11 +49,10 @@ struct GSUB : GSUBGPOS }; -} } -struct GSUB_accelerator_t : Layout::GSUB::GSUB::accelerator_t { - GSUB_accelerator_t (hb_face_t *face) : Layout::GSUB::GSUB::accelerator_t (face) {} +struct GSUB_accelerator_t : Layout::GSUB::accelerator_t { + GSUB_accelerator_t (hb_face_t *face) : Layout::GSUB::accelerator_t (face) {} }; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh index 0448d925d12..b0fdd91dc22 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh @@ -5,18 +5,20 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { +template struct Ligature { protected: - HBGlyphID16 ligGlyph; /* GlyphID of ligature to substitute */ - HeadlessArrayOf + typename Types::HBGlyphID + ligGlyph; /* GlyphID of ligature to substitute */ + HeadlessArrayOf component; /* Array of component GlyphIDs--start * with the second component--ordered * in writing direction */ public: - DEFINE_SIZE_ARRAY (4, component); + DEFINE_SIZE_ARRAY (Types::size + 2, component); bool sanitize (hb_sanitize_context_t *c) const { @@ -62,7 +64,24 @@ struct Ligature * as a "ligated" substitution. */ if (unlikely (count == 1)) { + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %u (ligature substitution)", + c->buffer->idx); + } + c->replace_glyph (ligGlyph); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (ligature substitution)", + c->buffer->idx - 1u); + } + return_trace (true); } @@ -83,6 +102,31 @@ struct Ligature return_trace (false); } + unsigned pos = 0; + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + unsigned delta = c->buffer->sync_so_far (); + + pos = c->buffer->idx; + + char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0}; + char *p = buf; + + match_end += delta; + for (unsigned i = 0; i < count; i++) + { + match_positions[i] += delta; + if (i) + *p++ = ','; + snprintf (p, sizeof(buf) - (p - buf), "%u", match_positions[i]); + p += strlen(p); + } + + c->buffer->message (c->font, + "ligating glyphs at %s", + buf); + } + ligate_input (c, count, match_positions, @@ -90,6 +134,14 @@ struct Ligature ligGlyph, total_component_count); + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "ligated glyph at %u", + pos); + } + return_trace (true); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSet.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSet.hh index 185b324b35b..637cec71377 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSet.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSet.hh @@ -6,12 +6,13 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { +template struct LigatureSet { protected: - Array16OfOffset16To + Array16OfOffset16To> ligature; /* Array LigatureSet tables * ordered by preference */ public: @@ -28,7 +29,7 @@ struct LigatureSet return + hb_iter (ligature) | hb_map (hb_add (this)) - | hb_map ([glyphs] (const Ligature &_) { return _.intersects (glyphs); }) + | hb_map ([glyphs] (const Ligature &_) { return _.intersects (glyphs); }) | hb_any ; } @@ -37,7 +38,7 @@ struct LigatureSet { + hb_iter (ligature) | hb_map (hb_add (this)) - | hb_apply ([c] (const Ligature &_) { _.closure (c); }) + | hb_apply ([c] (const Ligature &_) { _.closure (c); }) ; } @@ -45,7 +46,7 @@ struct LigatureSet { + hb_iter (ligature) | hb_map (hb_add (this)) - | hb_apply ([c] (const Ligature &_) { _.collect_glyphs (c); }) + | hb_apply ([c] (const Ligature &_) { _.collect_glyphs (c); }) ; } @@ -54,7 +55,7 @@ struct LigatureSet return + hb_iter (ligature) | hb_map (hb_add (this)) - | hb_map ([c] (const Ligature &_) { return _.would_apply (c); }) + | hb_map ([c] (const Ligature &_) { return _.would_apply (c); }) | hb_any ; } @@ -65,7 +66,7 @@ struct LigatureSet unsigned int num_ligs = ligature.len; for (unsigned int i = 0; i < num_ligs; i++) { - const Ligature &lig = this+ligature[i]; + const auto &lig = this+ligature[i]; if (lig.apply (c)) return_trace (true); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubst.hh index a029bf5e9f4..cffa910295f 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubst.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubst.hh @@ -6,28 +6,37 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { struct LigatureSubst { protected: union { - HBUINT16 format; /* Format identifier */ - LigatureSubstFormat1 format1; + HBUINT16 format; /* Format identifier */ + LigatureSubstFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + LigatureSubstFormat1_2 format2; +#endif } u; public: template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); - if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif default:return_trace (c->default_return_value ()); } } + /* TODO This function is only used by small GIDs, and not updated to 24bit GIDs. Should + * be done by using iterators. While at it perhaps using iterator of arrays of hb_codepoint_t + * instead. */ bool serialize (hb_serialize_context_t *c, hb_sorted_array_t first_glyphs, hb_array_t ligature_per_first_glyph_count_list, @@ -49,6 +58,9 @@ struct LigatureSubst default:return_trace (false); } } + + /* TODO subset() should choose format. */ + }; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh index 19dfe984694..32b642c38ad 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh @@ -6,20 +6,21 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { -struct LigatureSubstFormat1 +template +struct LigatureSubstFormat1_2 { protected: HBUINT16 format; /* Format identifier--format = 1 */ - Offset16To + typename Types::template OffsetTo coverage; /* Offset to Coverage table--from * beginning of Substitution table */ - Array16OfOffset16To + Array16Of>> ligatureSet; /* Array LigatureSet tables * ordered by Coverage Index */ public: - DEFINE_SIZE_ARRAY (6, ligatureSet); + DEFINE_SIZE_ARRAY (4 + Types::size, ligatureSet); bool sanitize (hb_sanitize_context_t *c) const { @@ -33,7 +34,7 @@ struct LigatureSubstFormat1 + hb_zip (this+coverage, ligatureSet) | hb_filter (*glyphs, hb_first) | hb_map (hb_second) - | hb_map ([this, glyphs] (const Offset16To &_) + | hb_map ([this, glyphs] (const typename Types::template OffsetTo> &_) { return (this+_).intersects (glyphs); }) | hb_any ; @@ -48,7 +49,7 @@ struct LigatureSubstFormat1 | hb_filter (c->parent_active_glyphs (), hb_first) | hb_map (hb_second) | hb_map (hb_add (this)) - | hb_apply ([c] (const LigatureSet &_) { _.closure (c); }) + | hb_apply ([c] (const LigatureSet &_) { _.closure (c); }) ; } @@ -62,7 +63,7 @@ struct LigatureSubstFormat1 + hb_zip (this+coverage, ligatureSet) | hb_map (hb_second) | hb_map (hb_add (this)) - | hb_apply ([c] (const LigatureSet &_) { _.collect_glyphs (c); }) + | hb_apply ([c] (const LigatureSet &_) { _.collect_glyphs (c); }) ; } @@ -73,7 +74,7 @@ struct LigatureSubstFormat1 unsigned int index = (this+coverage).get_coverage (c->glyphs[0]); if (likely (index == NOT_COVERED)) return false; - const LigatureSet &lig_set = this+ligatureSet[index]; + const auto &lig_set = this+ligatureSet[index]; return lig_set.would_apply (c); } @@ -84,7 +85,7 @@ struct LigatureSubstFormat1 unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); if (likely (index == NOT_COVERED)) return_trace (false); - const LigatureSet &lig_set = this+ligatureSet[index]; + const auto &lig_set = this+ligatureSet[index]; return_trace (lig_set.apply (c)); } @@ -128,7 +129,7 @@ struct LigatureSubstFormat1 hb_set_t new_coverage; + hb_zip (this+coverage, hb_iter (ligatureSet) | hb_map (hb_add (this))) | hb_filter (glyphset, hb_first) - | hb_filter ([&] (const LigatureSet& _) { + | hb_filter ([&] (const LigatureSet& _) { return _.intersects (&glyphset); }, hb_second) | hb_map (hb_first) diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubst.hh index b2891755048..cf3d754e3cc 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubst.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubst.hh @@ -6,14 +6,17 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { struct MultipleSubst { protected: union { - HBUINT16 format; /* Format identifier */ - MultipleSubstFormat1 format1; + HBUINT16 format; /* Format identifier */ + MultipleSubstFormat1_2 format1; +#ifndef HB_NO_BEYOND_64K + MultipleSubstFormat1_2 format2; +#endif } u; public: @@ -21,28 +24,34 @@ struct MultipleSubst template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); - if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif default:return_trace (c->default_return_value ()); } } + template bool serialize (hb_serialize_context_t *c, - hb_sorted_array_t glyphs, - hb_array_t substitute_len_list, - hb_array_t substitute_glyphs_list) + Iterator it) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (u.format))) return_trace (false); unsigned int format = 1; u.format = format; switch (u.format) { - case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, substitute_glyphs_list)); + case 1: return_trace (u.format1.serialize (c, it)); default:return_trace (false); } } + + /* TODO subset() should choose format. */ + }; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubstFormat1.hh index 54c6dc84783..4a9972c29cc 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubstFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubstFormat1.hh @@ -6,20 +6,21 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { -struct MultipleSubstFormat1 +template +struct MultipleSubstFormat1_2 { protected: HBUINT16 format; /* Format identifier--format = 1 */ - Offset16To + typename Types::template OffsetTo coverage; /* Offset to Coverage table--from * beginning of Substitution table */ - Array16OfOffset16To + Array16Of>> sequence; /* Array of Sequence tables * ordered by Coverage Index */ public: - DEFINE_SIZE_ARRAY (6, sequence); + DEFINE_SIZE_ARRAY (4 + Types::size, sequence); bool sanitize (hb_sanitize_context_t *c) const { @@ -39,7 +40,7 @@ struct MultipleSubstFormat1 | hb_filter (c->parent_active_glyphs (), hb_first) | hb_map (hb_second) | hb_map (hb_add (this)) - | hb_apply ([c] (const Sequence &_) { _.closure (c); }) + | hb_apply ([c] (const Sequence &_) { _.closure (c); }) ; } @@ -51,7 +52,7 @@ struct MultipleSubstFormat1 + hb_zip (this+coverage, sequence) | hb_map (hb_second) | hb_map (hb_add (this)) - | hb_apply ([c] (const Sequence &_) { _.collect_glyphs (c); }) + | hb_apply ([c] (const Sequence &_) { _.collect_glyphs (c); }) ; } @@ -70,22 +71,31 @@ struct MultipleSubstFormat1 return_trace ((this+sequence[index]).apply (c)); } + template bool serialize (hb_serialize_context_t *c, - hb_sorted_array_t glyphs, - hb_array_t substitute_len_list, - hb_array_t substitute_glyphs_list) + Iterator it) { TRACE_SERIALIZE (this); + auto sequences = + + it + | hb_map (hb_second) + ; + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; if (unlikely (!c->extend_min (this))) return_trace (false); - if (unlikely (!sequence.serialize (c, glyphs.length))) return_trace (false); - for (unsigned int i = 0; i < glyphs.length; i++) + + if (unlikely (!sequence.serialize (c, sequences.length))) return_trace (false); + + for (auto& pair : hb_zip (sequences, sequence)) { - unsigned int substitute_len = substitute_len_list[i]; - if (unlikely (!sequence[i] - .serialize_serialize (c, substitute_glyphs_list.sub_array (0, substitute_len)))) + if (unlikely (!pair.second + .serialize_serialize (c, pair.first))) return_trace (false); - substitute_glyphs_list += substitute_len; } + return_trace (coverage.serialize_serialize (c, glyphs)); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubst.hh index 435d80fd310..5ad463fea79 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubst.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubst.hh @@ -6,7 +6,7 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { struct ReverseChainSingleSubst { @@ -20,8 +20,8 @@ struct ReverseChainSingleSubst template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); - if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); default:return_trace (c->default_return_value ()); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh index 7a79a9df25e..73f222746e5 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh @@ -5,7 +5,7 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { struct ReverseChainSingleSubstFormat1 { @@ -33,10 +33,10 @@ struct ReverseChainSingleSubstFormat1 TRACE_SANITIZE (this); if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this))) return_trace (false); - const Array16OfOffset16To &lookahead = StructAfter> (backtrack); + const auto &lookahead = StructAfter (backtrack); if (!lookahead.sanitize (c, this)) return_trace (false); - const Array16Of &substitute = StructAfter> (lookahead); + const auto &substitute = StructAfter (lookahead); return_trace (substitute.sanitize (c)); } @@ -45,7 +45,7 @@ struct ReverseChainSingleSubstFormat1 if (!(this+coverage).intersects (glyphs)) return false; - const Array16OfOffset16To &lookahead = StructAfter> (backtrack); + const auto &lookahead = StructAfter (backtrack); unsigned int count; @@ -69,8 +69,8 @@ struct ReverseChainSingleSubstFormat1 { if (!intersects (c->glyphs)) return; - const Array16OfOffset16To &lookahead = StructAfter> (backtrack); - const Array16Of &substitute = StructAfter> (lookahead); + const auto &lookahead = StructAfter (backtrack); + const auto &substitute = StructAfter (lookahead); + hb_zip (this+coverage, substitute) | hb_filter (c->parent_active_glyphs (), hb_first) @@ -91,12 +91,12 @@ struct ReverseChainSingleSubstFormat1 for (unsigned int i = 0; i < count; i++) if (unlikely (!(this+backtrack[i]).collect_coverage (c->before))) return; - const Array16OfOffset16To &lookahead = StructAfter> (backtrack); + const auto &lookahead = StructAfter (backtrack); count = lookahead.len; for (unsigned int i = 0; i < count; i++) if (unlikely (!(this+lookahead[i]).collect_coverage (c->after))) return; - const Array16Of &substitute = StructAfter> (lookahead); + const auto &substitute = StructAfter (lookahead); count = substitute.len; c->output->add_array (substitute.arrayZ, substitute.len); } @@ -115,8 +115,8 @@ struct ReverseChainSingleSubstFormat1 unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); if (likely (index == NOT_COVERED)) return_trace (false); - const Array16OfOffset16To &lookahead = StructAfter> (backtrack); - const Array16Of &substitute = StructAfter> (lookahead); + const auto &lookahead = StructAfter (backtrack); + const auto &substitute = StructAfter (lookahead); if (unlikely (index >= substitute.len)) return_trace (false); @@ -131,7 +131,23 @@ struct ReverseChainSingleSubstFormat1 c->buffer->idx + 1, &end_index)) { c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replacing glyph at %u (reverse chaining substitution)", + c->buffer->idx); + } + c->replace_glyph_inplace (substitute[index]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (reverse chaining substitution)", + c->buffer->idx); + } + /* Note: We DON'T decrease buffer->idx. The main loop does it * for us. This is useful for preventing surprises if someone * calls us through a Context lookup. */ @@ -206,8 +222,8 @@ struct ReverseChainSingleSubstFormat1 const hb_set_t &glyphset = *c->plan->glyphset_gsub (); const hb_map_t &glyph_map = *c->plan->glyph_map; - const Array16OfOffset16To &lookahead = StructAfter> (backtrack); - const Array16Of &substitute = StructAfter> (lookahead); + const auto &lookahead = StructAfter (backtrack); + const auto &substitute = StructAfter (lookahead); auto it = + hb_zip (this+coverage, substitute) diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Sequence.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Sequence.hh index ebd451e6ba3..62d68160c7b 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Sequence.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Sequence.hh @@ -5,12 +5,13 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { +template struct Sequence { protected: - Array16Of + Array16Of substitute; /* String of GlyphIDs to substitute */ public: DEFINE_SIZE_ARRAY (2, substitute); @@ -39,17 +40,58 @@ struct Sequence * as a "multiplied" substitution. */ if (unlikely (count == 1)) { + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %u (multiple substitution)", + c->buffer->idx); + } + c->replace_glyph (substitute.arrayZ[0]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (multiple subtitution)", + c->buffer->idx - 1u); + } + return_trace (true); } /* Spec disallows this, but Uniscribe allows it. * https://github.com/harfbuzz/harfbuzz/issues/253 */ else if (unlikely (count == 0)) { + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "deleting glyph at %u (multiple substitution)", + c->buffer->idx); + } + c->buffer->delete_glyph (); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "deleted glyph at %u (multiple substitution)", + c->buffer->idx); + } + return_trace (true); } + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "multiplying glyph at %u", + c->buffer->idx); + } + unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ? HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0; unsigned lig_id = _hb_glyph_info_get_lig_id (&c->buffer->cur()); @@ -64,6 +106,26 @@ struct Sequence } c->buffer->skip_glyph (); + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + + char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0}; + char *p = buf; + + for (unsigned i = c->buffer->idx - count; i < c->buffer->idx; i++) + { + if (buf < p) + *p++ = ','; + snprintf (p, sizeof(buf) - (p - buf), "%u", i); + p += strlen(p); + } + + c->buffer->message (c->font, + "multiplied glyphs at %s", + buf); + } + return_trace (true); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubst.hh index 786428fe453..304d1928e23 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubst.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubst.hh @@ -7,15 +7,19 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { struct SingleSubst { protected: union { - HBUINT16 format; /* Format identifier */ - SingleSubstFormat1 format1; - SingleSubstFormat2 format2; + HBUINT16 format; /* Format identifier */ + SingleSubstFormat1_3 format1; + SingleSubstFormat2_4 format2; +#ifndef HB_NO_BEYOND_64K + SingleSubstFormat1_3 format3; + SingleSubstFormat2_4 format4; +#endif } u; public: @@ -23,11 +27,15 @@ struct SingleSubst template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { + if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); TRACE_DISPATCH (this, u.format); - if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...)); +#endif default:return_trace (c->default_return_value ()); } } @@ -45,11 +53,24 @@ struct SingleSubst if (glyphs) { format = 1; + hb_codepoint_t mask = 0xFFFFu; + +#ifndef HB_NO_BEYOND_64K + if (+ glyphs + | hb_map_retains_sorting (hb_first) + | hb_filter ([] (hb_codepoint_t gid) { return gid > 0xFFFFu; })) + { + format += 2; + mask = 0xFFFFFFu; + } +#endif + auto get_delta = [=] (hb_codepoint_pair_t _) - { return (unsigned) (_.second - _.first) & 0xFFFF; }; + { return (unsigned) (_.second - _.first) & mask; }; delta = get_delta (*glyphs); - if (!hb_all (++(+glyphs), delta, get_delta)) format = 2; + if (!hb_all (++(+glyphs), delta, get_delta)) format += 1; } + u.format = format; switch (u.format) { case 1: return_trace (u.format1.serialize (c, @@ -57,6 +78,13 @@ struct SingleSubst | hb_map_retains_sorting (hb_first), delta)); case 2: return_trace (u.format2.serialize (c, glyphs)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (u.format3.serialize (c, + + glyphs + | hb_map_retains_sorting (hb_first), + delta)); + case 4: return_trace (u.format4.serialize (c, glyphs)); +#endif default:return_trace (false); } } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh index 3c6b2954cec..1fe9488e212 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh @@ -5,20 +5,22 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { -struct SingleSubstFormat1 +template +struct SingleSubstFormat1_3 { protected: HBUINT16 format; /* Format identifier--format = 1 */ - Offset16To + typename Types::template OffsetTo coverage; /* Offset to Coverage table--from * beginning of Substitution table */ - HBUINT16 deltaGlyphID; /* Add to original GlyphID to get + typename Types::HBUINT + deltaGlyphID; /* Add to original GlyphID to get * substitute GlyphID, modulo 0x10000 */ public: - DEFINE_SIZE_STATIC (6); + DEFINE_SIZE_STATIC (2 + 2 * Types::size); bool sanitize (hb_sanitize_context_t *c) const { @@ -26,6 +28,9 @@ struct SingleSubstFormat1 return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c)); } + hb_codepoint_t get_mask () const + { return (1 << (8 * Types::size)) - 1; } + bool intersects (const hb_set_t *glyphs) const { return (this+coverage).intersects (glyphs); } @@ -34,14 +39,33 @@ struct SingleSubstFormat1 void closure (hb_closure_context_t *c) const { - unsigned d = deltaGlyphID; - - + hb_iter (this+coverage) - | hb_filter (c->parent_active_glyphs ()) - | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; }) + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + /* Help fuzzer avoid this function as much. */ + unsigned pop = (this+coverage).get_population (); + if (pop >= mask) + return; + + hb_set_t intersection; + (this+coverage).intersect_set (c->parent_active_glyphs (), intersection); + + /* In degenerate fuzzer-found fonts, but not real fonts, + * this table can keep adding new glyphs in each round of closure. + * Refuse to close-over, if it maps glyph range to overlapping range. */ + hb_codepoint_t min_before = intersection.get_min (); + hb_codepoint_t max_before = intersection.get_max (); + hb_codepoint_t min_after = (min_before + d) & mask; + hb_codepoint_t max_after = (max_before + d) & mask; + if (intersection.get_population () == max_before - min_before + 1 && + ((min_before <= min_after && min_after <= max_before) || + (min_before <= max_after && max_after <= max_before))) + return; + + + hb_iter (intersection) + | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; }) | hb_sink (c->output) ; - } void closure_lookups (hb_closure_lookups_context_t *c) const {} @@ -49,9 +73,11 @@ struct SingleSubstFormat1 void collect_glyphs (hb_collect_glyphs_context_t *c) const { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; - unsigned d = deltaGlyphID; + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + hb_iter (this+coverage) - | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; }) + | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; }) | hb_sink (c->output) ; } @@ -68,11 +94,28 @@ struct SingleSubstFormat1 unsigned int index = (this+coverage).get_coverage (glyph_id); if (likely (index == NOT_COVERED)) return_trace (false); - /* According to the Adobe Annotated OpenType Suite, result is always - * limited to 16bit. */ - glyph_id = (glyph_id + deltaGlyphID) & 0xFFFFu; + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + glyph_id = (glyph_id + d) & mask; + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %u (single substitution)", + c->buffer->idx); + } + c->replace_glyph (glyph_id); + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (single substitution)", + c->buffer->idx - 1u); + } + return_trace (true); } @@ -95,14 +138,17 @@ struct SingleSubstFormat1 const hb_set_t &glyphset = *c->plan->glyphset_gsub (); const hb_map_t &glyph_map = *c->plan->glyph_map; - hb_codepoint_t delta = deltaGlyphID; + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + hb_set_t intersection; + (this+coverage).intersect_set (glyphset, intersection); auto it = - + hb_iter (this+coverage) - | hb_filter (glyphset) - | hb_map_retains_sorting ([&] (hb_codepoint_t g) { + + hb_iter (intersection) + | hb_map_retains_sorting ([d, mask] (hb_codepoint_t g) { return hb_codepoint_pair_t (g, - (g + delta) & 0xFFFF); }) + (g + d) & mask); }) | hb_filter (glyphset, hb_second) | hb_map_retains_sorting ([&] (hb_codepoint_pair_t p) -> hb_codepoint_pair_t { return hb_pair (glyph_map[p.first], glyph_map[p.second]); }) diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh index df75bb52bbc..908cc2af158 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh @@ -5,21 +5,22 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { -struct SingleSubstFormat2 +template +struct SingleSubstFormat2_4 { protected: HBUINT16 format; /* Format identifier--format = 2 */ - Offset16To + typename Types::template OffsetTo coverage; /* Offset to Coverage table--from * beginning of Substitution table */ - Array16Of + Array16Of substitute; /* Array of substitute * GlyphIDs--ordered by Coverage Index */ public: - DEFINE_SIZE_ARRAY (6, substitute); + DEFINE_SIZE_ARRAY (4 + Types::size, substitute); bool sanitize (hb_sanitize_context_t *c) const { @@ -35,12 +36,27 @@ struct SingleSubstFormat2 void closure (hb_closure_context_t *c) const { - + hb_zip (this+coverage, substitute) - | hb_filter (c->parent_active_glyphs (), hb_first) + auto &cov = this+coverage; + auto &glyph_set = c->parent_active_glyphs (); + + if (substitute.len > glyph_set.get_population () * 4) + { + for (auto g : glyph_set) + { + unsigned i = cov.get_coverage (g); + if (i == NOT_COVERED || i >= substitute.len) + continue; + c->output->add (substitute.arrayZ[i]); + } + + return; + } + + + hb_zip (cov, substitute) + | hb_filter (glyph_set, hb_first) | hb_map (hb_second) | hb_sink (c->output) ; - } void closure_lookups (hb_closure_lookups_context_t *c) const {} @@ -67,8 +83,23 @@ struct SingleSubstFormat2 if (unlikely (index >= substitute.len)) return_trace (false); + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %u (single substitution)", + c->buffer->idx); + } + c->replace_glyph (substitute[index]); + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %u (single substitution)", + c->buffer->idx - 1u); + } + return_trace (true); } @@ -103,7 +134,7 @@ struct SingleSubstFormat2 + hb_zip (this+coverage, substitute) | hb_filter (glyphset, hb_first) | hb_filter (glyphset, hb_second) - | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_codepoint_pair_t + | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_codepoint_pair_t { return hb_pair (glyph_map[p.first], glyph_map[p.second]); }) ; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookup.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookup.hh index 8fb3b550976..5796cc3be57 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookup.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookup.hh @@ -6,7 +6,7 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { struct SubstLookup : Lookup { @@ -25,7 +25,7 @@ struct SubstLookup : Lookup { unsigned int type = get_type (); if (unlikely (type == SubTable::Extension)) - return reinterpret_cast (get_subtable (0)).is_reverse (); + return get_subtable (0).u.extension.is_reverse (); return lookup_type_is_reverse (type); } @@ -98,10 +98,15 @@ struct SubstLookup : Lookup return dispatch (c); } + template bool serialize_single (hb_serialize_context_t *c, uint32_t lookup_props, - hb_sorted_array_t glyphs, - hb_array_t substitutes) + Glyphs glyphs, + Substitutes substitutes) { TRACE_SERIALIZE (this); if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false); @@ -114,19 +119,16 @@ struct SubstLookup : Lookup return_trace (false); } - bool serialize_multiple (hb_serialize_context_t *c, - uint32_t lookup_props, - hb_sorted_array_t glyphs, - hb_array_t substitute_len_list, - hb_array_t substitute_glyphs_list) + template + bool serialize (hb_serialize_context_t *c, + uint32_t lookup_props, + Iterator it) { TRACE_SERIALIZE (this); if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false); if (c->push ()->u.multiple. - serialize (c, - glyphs, - substitute_len_list, - substitute_glyphs_list)) + serialize (c, it)) { c->add_link (get_subtables ()[0], c->pop_pack ()); return_trace (true); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookupSubTable.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookupSubTable.hh index 53e963e2a29..a525fba0399 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookupSubTable.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SubstLookupSubTable.hh @@ -13,7 +13,7 @@ namespace OT { namespace Layout { -namespace GSUB { +namespace GSUB_impl { struct SubstLookupSubTable { diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh new file mode 100644 index 00000000000..6a43403e94b --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh @@ -0,0 +1,66 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_TYPES_HH +#define OT_LAYOUT_TYPES_HH + +namespace OT { +namespace Layout { + +struct SmallTypes { + static constexpr unsigned size = 2; + using large_int = uint32_t; + using HBUINT = HBUINT16; + using HBGlyphID = HBGlyphID16; + using Offset = Offset16; + template + using OffsetTo = OT::Offset16To; + template + using ArrayOf = OT::Array16Of; + template + using SortedArrayOf = OT::SortedArray16Of; +}; + +struct MediumTypes { + static constexpr unsigned size = 3; + using large_int = uint64_t; + using HBUINT = HBUINT24; + using HBGlyphID = HBGlyphID24; + using Offset = Offset24; + template + using OffsetTo = OT::Offset24To; + template + using ArrayOf = OT::Array24Of; + template + using SortedArrayOf = OT::SortedArray24Of; +}; + +} +} + +#endif /* OT_LAYOUT_TYPES_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/CompositeGlyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/CompositeGlyph.hh index 7ebf64d0d5c..e24f3408ca6 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/CompositeGlyph.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/CompositeGlyph.hh @@ -3,6 +3,7 @@ #include "../../hb-open-type.hh" +#include "composite-iter.hh" namespace OT { @@ -25,13 +26,20 @@ struct CompositeGlyphRecord USE_MY_METRICS = 0x0200, OVERLAP_COMPOUND = 0x0400, SCALED_COMPONENT_OFFSET = 0x0800, - UNSCALED_COMPONENT_OFFSET = 0x1000 + UNSCALED_COMPONENT_OFFSET = 0x1000, +#ifndef HB_NO_BEYOND_64K + GID_IS_24BIT = 0x2000 +#endif }; public: unsigned int get_size () const { unsigned int size = min_size; + /* glyphIndex is 24bit instead of 16bit */ +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) size += HBGlyphID24::static_size - HBGlyphID16::static_size; +#endif /* arg1 and 2 are int16 */ if (flags & ARG_1_AND_2_ARE_WORDS) size += 4; /* arg1 and 2 are int8 */ @@ -60,7 +68,13 @@ struct CompositeGlyphRecord bool is_anchored () const { return !(flags & ARGS_ARE_XY_VALUES); } void get_anchor_points (unsigned int &point1, unsigned int &point2) const { - const HBUINT8 *p = &StructAfter (glyphIndex); + const auto *p = &StructAfter (flags); +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + p += HBGlyphID24::static_size; + else +#endif + p += HBGlyphID16::static_size; if (flags & ARG_1_AND_2_ARE_WORDS) { point1 = ((const HBUINT16 *) p)[0]; @@ -92,6 +106,67 @@ struct CompositeGlyphRecord } } + unsigned compile_with_deltas (const contour_point_t &p_delta, + char *out) const + { + const HBINT8 *p = &StructAfter (flags); +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + p += HBGlyphID24::static_size; + else +#endif + p += HBGlyphID16::static_size; + + unsigned len = get_size (); + unsigned len_before_val = (const char *)p - (const char *)this; + if (flags & ARG_1_AND_2_ARE_WORDS) + { + // no overflow, copy and update value with deltas + hb_memcpy (out, this, len); + + const HBINT16 *px = reinterpret_cast (p); + HBINT16 *o = reinterpret_cast (out + len_before_val); + o[0] = px[0] + roundf (p_delta.x); + o[1] = px[1] + roundf (p_delta.y); + } + else + { + int new_x = p[0] + roundf (p_delta.x); + int new_y = p[1] + roundf (p_delta.y); + if (new_x <= 127 && new_x >= -128 && + new_y <= 127 && new_y >= -128) + { + hb_memcpy (out, this, len); + HBINT8 *o = reinterpret_cast (out + len_before_val); + o[0] = new_x; + o[1] = new_y; + } + else + { + // int8 overflows after deltas applied + hb_memcpy (out, this, len_before_val); + + //update flags + CompositeGlyphRecord *o = reinterpret_cast (out); + o->flags = flags | ARG_1_AND_2_ARE_WORDS; + out += len_before_val; + + HBINT16 new_value; + new_value = new_x; + hb_memcpy (out, &new_value, HBINT16::static_size); + out += HBINT16::static_size; + + new_value = new_y; + hb_memcpy (out, &new_value, HBINT16::static_size); + out += HBINT16::static_size; + + hb_memcpy (out, p+2, len - len_before_val - 2); + len += 2; + } + } + return len; + } + protected: bool scaled_offsets () const { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; } @@ -101,8 +176,14 @@ struct CompositeGlyphRecord matrix[0] = matrix[3] = 1.f; matrix[1] = matrix[2] = 0.f; + const auto *p = &StructAfter (flags); +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + p += HBGlyphID24::static_size; + else +#endif + p += HBGlyphID16::static_size; int tx, ty; - const HBINT8 *p = &StructAfter (glyphIndex); if (flags & ARG_1_AND_2_ARE_WORDS) { tx = *(const HBINT16 *) p; @@ -145,62 +226,35 @@ struct CompositeGlyphRecord } public: - HBUINT16 flags; - HBGlyphID16 glyphIndex; - public: - DEFINE_SIZE_MIN (4); -}; - -struct composite_iter_t : hb_iter_with_fallback_t -{ - typedef const CompositeGlyphRecord *__item_t__; - composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) : - glyph (glyph_), current (nullptr), current_size (0) + hb_codepoint_t get_gid () const { - set_current (current_); +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + return StructAfter (flags); + else +#endif + return StructAfter (flags); } - - composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {} - - item_t __item__ () const { return *current; } - bool __more__ () const { return current; } - void __next__ () + void set_gid (hb_codepoint_t gid) { - if (!current->has_more ()) { current = nullptr; return; } - - set_current (&StructAtOffset (current, current_size)); - } - composite_iter_t __end__ () const { return composite_iter_t (); } - bool operator != (const composite_iter_t& o) const - { return current != o.current; } - - - void set_current (__item_t__ current_) - { - if (!glyph.check_range (current_, CompositeGlyphRecord::min_size)) - { - current = nullptr; - current_size = 0; - return; - } - unsigned size = current_->get_size (); - if (!glyph.check_range (current_, size)) - { - current = nullptr; - current_size = 0; - return; - } - - current = current_; - current_size = size; +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + StructAfter (flags) = gid; + else +#endif + /* TODO assert? */ + StructAfter (flags) = gid; } - private: - hb_bytes_t glyph; - __item_t__ current; - unsigned current_size; + protected: + HBUINT16 flags; + HBUINT24 pad; + public: + DEFINE_SIZE_MIN (4); }; +using composite_iter_t = composite_iter_tmpl; + struct CompositeGlyph { const GlyphHeader &header; @@ -248,6 +302,63 @@ struct CompositeGlyph return; glyph_chain.set_overlaps_flag (); } + + bool compile_bytes_with_deltas (const hb_bytes_t &source_bytes, + const contour_point_vector_t &deltas, + hb_bytes_t &dest_bytes /* OUT */) + { + if (source_bytes.length <= GlyphHeader::static_size || + header.numberOfContours != -1) + { + dest_bytes = hb_bytes_t (); + return true; + } + + unsigned source_len = source_bytes.length - GlyphHeader::static_size; + + /* try to allocate more memories than source glyph bytes + * in case that there might be an overflow for int8 value + * and we would need to use int16 instead */ + char *o = (char *) hb_calloc (source_len + source_len/2, sizeof (char)); + if (unlikely (!o)) return false; + + const CompositeGlyphRecord *c = reinterpret_cast (source_bytes.arrayZ + GlyphHeader::static_size); + auto it = composite_iter_t (hb_bytes_t ((const char *)c, source_len), c); + + char *p = o; + unsigned i = 0, source_comp_len = 0; + for (const auto &component : it) + { + /* last 4 points in deltas are phantom points and should not be included */ + if (i >= deltas.length - 4) return false; + + unsigned comp_len = component.get_size (); + if (component.is_anchored ()) + { + hb_memcpy (p, &component, comp_len); + p += comp_len; + } + else + { + unsigned new_len = component.compile_with_deltas (deltas[i], p); + p += new_len; + } + i++; + source_comp_len += comp_len; + } + + //copy instructions if any + if (source_len > source_comp_len) + { + unsigned instr_len = source_len - source_comp_len; + hb_memcpy (p, (const char *)c + source_comp_len, instr_len); + p += instr_len; + } + + unsigned len = p - o; + dest_bytes = hb_bytes_t (o, len); + return true; + } }; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh index 3d2095ac2df..7cd1f571bbc 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh @@ -7,6 +7,8 @@ #include "GlyphHeader.hh" #include "SimpleGlyph.hh" #include "CompositeGlyph.hh" +#include "VarCompositeGlyph.hh" +#include "coord-setter.hh" namespace OT { @@ -27,7 +29,7 @@ enum phantom_point_index_t struct Glyph { - enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE }; + enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE, VAR_COMPOSITE }; public: composite_iter_t get_composite_iterator () const @@ -35,6 +37,11 @@ struct Glyph if (type != COMPOSITE) return composite_iter_t (); return CompositeGlyph (*header, bytes).iter (); } + var_composite_iter_t get_var_composite_iterator () const + { + if (type != VAR_COMPOSITE) return var_composite_iter_t (); + return VarCompositeGlyph (*header, bytes).iter (); + } const hb_bytes_t trim_padding () const { @@ -72,22 +79,212 @@ struct Glyph } } + void update_mtx (const hb_subset_plan_t *plan, + int xMin, int xMax, + int yMin, int yMax, + const contour_point_vector_t &all_points) const + { + hb_codepoint_t new_gid = 0; + if (!plan->new_gid_for_old_gid (gid, &new_gid)) + return; + + if (type != EMPTY) + { + plan->bounds_width_map.set (new_gid, xMax - xMin); + plan->bounds_height_map.set (new_gid, yMax - yMin); + } + + unsigned len = all_points.length; + float leftSideX = all_points[len - 4].x; + float rightSideX = all_points[len - 3].x; + float topSideY = all_points[len - 2].y; + float bottomSideY = all_points[len - 1].y; + + signed hori_aw = roundf (rightSideX - leftSideX); + if (hori_aw < 0) hori_aw = 0; + int lsb = roundf (xMin - leftSideX); + plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb)); + //flag value should be computed using non-empty glyphs + if (type != EMPTY && lsb != xMin) + plan->head_maxp_info.allXMinIsLsb = false; + + signed vert_aw = roundf (topSideY - bottomSideY); + if (vert_aw < 0) vert_aw = 0; + int tsb = roundf (topSideY - yMax); + plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb)); + } + + bool compile_header_bytes (const hb_subset_plan_t *plan, + const contour_point_vector_t &all_points, + hb_bytes_t &dest_bytes /* OUT */) const + { + GlyphHeader *glyph_header = nullptr; + if (!plan->pinned_at_default && type != EMPTY && all_points.length >= 4) + { + glyph_header = (GlyphHeader *) hb_calloc (1, GlyphHeader::static_size); + if (unlikely (!glyph_header)) return false; + } + + float xMin = 0, xMax = 0; + float yMin = 0, yMax = 0; + if (all_points.length > 4) + { + xMin = xMax = all_points[0].x; + yMin = yMax = all_points[0].y; + } + + for (unsigned i = 1; i < all_points.length - 4; i++) + { + float x = all_points[i].x; + float y = all_points[i].y; + xMin = hb_min (xMin, x); + xMax = hb_max (xMax, x); + yMin = hb_min (yMin, y); + yMax = hb_max (yMax, y); + } + + update_mtx (plan, roundf (xMin), roundf (xMax), roundf (yMin), roundf (yMax), all_points); + + int rounded_xMin = roundf (xMin); + int rounded_xMax = roundf (xMax); + int rounded_yMin = roundf (yMin); + int rounded_yMax = roundf (yMax); + + if (type != EMPTY) + { + plan->head_maxp_info.xMin = hb_min (plan->head_maxp_info.xMin, rounded_xMin); + plan->head_maxp_info.yMin = hb_min (plan->head_maxp_info.yMin, rounded_yMin); + plan->head_maxp_info.xMax = hb_max (plan->head_maxp_info.xMax, rounded_xMax); + plan->head_maxp_info.yMax = hb_max (plan->head_maxp_info.yMax, rounded_yMax); + } + + /* when pinned at default, no need to compile glyph header + * and for empty glyphs: all_points only include phantom points. + * just update metrics and then return */ + if (!glyph_header) + return true; + + glyph_header->numberOfContours = header->numberOfContours; + + glyph_header->xMin = rounded_xMin; + glyph_header->yMin = rounded_yMin; + glyph_header->xMax = rounded_xMax; + glyph_header->yMax = rounded_yMax; + + dest_bytes = hb_bytes_t ((const char *)glyph_header, GlyphHeader::static_size); + return true; + } + + bool compile_bytes_with_deltas (const hb_subset_plan_t *plan, + hb_font_t *font, + const glyf_accelerator_t &glyf, + hb_bytes_t &dest_start, /* IN/OUT */ + hb_bytes_t &dest_end /* OUT */) + { + contour_point_vector_t all_points, deltas; + unsigned composite_contours = 0; + head_maxp_info_t *head_maxp_info_p = &plan->head_maxp_info; + unsigned *composite_contours_p = &composite_contours; + + // don't compute head/maxp values when glyph has no contours(type is EMPTY) + // also ignore .notdef glyph when --notdef-outline is not enabled + if (type == EMPTY || + (gid == 0 && !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))) + { + head_maxp_info_p = nullptr; + composite_contours_p = nullptr; + } + + if (!get_points (font, glyf, all_points, &deltas, head_maxp_info_p, composite_contours_p, false, false)) + return false; + + // .notdef, set type to empty so we only update metrics and don't compile bytes for + // it + if (gid == 0 && + !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) + { + type = EMPTY; + dest_start = hb_bytes_t (); + dest_end = hb_bytes_t (); + } + + //dont compile bytes when pinned at default, just recalculate bounds + if (!plan->pinned_at_default) { + switch (type) { + case COMPOSITE: + if (!CompositeGlyph (*header, bytes).compile_bytes_with_deltas (dest_start, + deltas, + dest_end)) + return false; + break; + case SIMPLE: + if (!SimpleGlyph (*header, bytes).compile_bytes_with_deltas (all_points, + plan->flags & HB_SUBSET_FLAGS_NO_HINTING, + dest_end)) + return false; + break; + default: + /* set empty bytes for empty glyph + * do not use source glyph's pointers */ + dest_start = hb_bytes_t (); + dest_end = hb_bytes_t (); + break; + } + } + + if (!compile_header_bytes (plan, all_points, dest_start)) + { + dest_end.fini (); + return false; + } + return true; + } + + /* Note: Recursively calls itself. * all_points includes phantom points */ template bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator, contour_point_vector_t &all_points /* OUT */, + contour_point_vector_t *deltas = nullptr, /* OUT */ + head_maxp_info_t * head_maxp_info = nullptr, /* OUT */ + unsigned *composite_contours = nullptr, /* OUT */ + bool shift_points_hori = true, + bool use_my_metrics = true, bool phantom_only = false, - unsigned int depth = 0) const + hb_array_t coords = hb_array_t (), + unsigned int depth = 0, + unsigned *edge_count = nullptr) const { if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false; + unsigned stack_edge_count = 0; + if (!edge_count) edge_count = &stack_edge_count; + if (unlikely (*edge_count > HB_GLYF_MAX_EDGE_COUNT)) return false; + (*edge_count)++; + + if (head_maxp_info) + { + head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth); + } + + if (!coords) + coords = hb_array (font->coords, font->num_coords); + contour_point_vector_t stack_points; bool inplace = type == SIMPLE && all_points.length == 0; /* Load into all_points if it's empty, as an optimization. */ contour_point_vector_t &points = inplace ? all_points : stack_points; switch (type) { + case SIMPLE: + if (depth == 0 && head_maxp_info) + head_maxp_info->maxContours = hb_max (head_maxp_info->maxContours, (unsigned) header->numberOfContours); + if (depth > 0 && composite_contours) + *composite_contours += (unsigned) header->numberOfContours; + if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only))) + return false; + break; case COMPOSITE: { /* pseudo component points for each component in composite glyph */ @@ -95,29 +292,36 @@ struct Glyph if (unlikely (!points.resize (num_points))) return false; break; } - case SIMPLE: - if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only))) - return false; +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: + { + for (auto &item : get_var_composite_iterator ()) + if (unlikely (!item.get_points (points))) return false; + } +#endif + default: break; } /* Init phantom points */ if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; - hb_array_t phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); + hb_array_t phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); { - int h_delta = (int) header->xMin - - glyf_accelerator.hmtx->get_side_bearing (gid); + int lsb = 0; + int h_delta = glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ? + (int) header->xMin - lsb : 0; + HB_UNUSED int tsb = 0; int v_orig = (int) header->yMax + #ifndef HB_NO_VERTICAL - glyf_accelerator.vmtx->get_side_bearing (gid) + ((void) glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb) #else 0 #endif ; - unsigned h_adv = glyf_accelerator.hmtx->get_advance (gid); + unsigned h_adv = glyf_accelerator.hmtx->get_advance_without_var_unscaled (gid); unsigned v_adv = #ifndef HB_NO_VERTICAL - glyf_accelerator.vmtx->get_advance (gid) + glyf_accelerator.vmtx->get_advance_without_var_unscaled (gid) #else - font->face->get_upem () #endif @@ -128,12 +332,33 @@ struct Glyph phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv; } + if (deltas != nullptr && depth == 0 && type == COMPOSITE) + { + if (unlikely (!deltas->resize (points.length))) return false; + deltas->copy_vector (points); + } + #ifndef HB_NO_VAR - glyf_accelerator.gvar->apply_deltas_to_points (gid, font, points.as_array ()); + glyf_accelerator.gvar->apply_deltas_to_points (gid, + coords, + points.as_array ()); #endif + // mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it + // with child glyphs' points + if (deltas != nullptr && depth == 0 && type == COMPOSITE) + { + for (unsigned i = 0 ; i < points.length; i++) + { + deltas->arrayZ[i].x = points.arrayZ[i].x - deltas->arrayZ[i].x; + deltas->arrayZ[i].y = points.arrayZ[i].y - deltas->arrayZ[i].y; + } + } + switch (type) { case SIMPLE: + if (depth == 0 && head_maxp_info) + head_maxp_info->maxPoints = hb_max (head_maxp_info->maxPoints, points.length - 4); if (!inplace) all_points.extend (points.as_array ()); break; @@ -144,13 +369,23 @@ struct Glyph for (auto &item : get_composite_iterator ()) { comp_points.reset (); - if (unlikely (!glyf_accelerator.glyph_for_gid (item.glyphIndex) - .get_points (font, glyf_accelerator, comp_points, - phantom_only, depth + 1))) + if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ()) + .get_points (font, + glyf_accelerator, + comp_points, + deltas, + head_maxp_info, + composite_contours, + shift_points_hori, + use_my_metrics, + phantom_only, + coords, + depth + 1, + edge_count))) return false; /* Copy phantom points from component if USE_MY_METRICS flag set */ - if (item.is_use_my_metrics ()) + if (use_my_metrics && item.is_use_my_metrics ()) for (unsigned int i = 0; i < PHANTOM_COUNT; i++) phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; @@ -174,18 +409,80 @@ struct Glyph } } - all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT)); + all_points.extend (comp_points.as_array ().sub_array (0, comp_points.length - PHANTOM_COUNT)); + + if (all_points.length > HB_GLYF_MAX_POINTS) + return false; comp_index++; } + if (head_maxp_info && depth == 0) + { + if (composite_contours) + head_maxp_info->maxCompositeContours = hb_max (head_maxp_info->maxCompositeContours, *composite_contours); + head_maxp_info->maxCompositePoints = hb_max (head_maxp_info->maxCompositePoints, all_points.length); + head_maxp_info->maxComponentElements = hb_max (head_maxp_info->maxComponentElements, comp_index); + } + all_points.extend (phantoms); + } break; +#ifndef HB_NO_VAR_COMPOSITES + case VAR_COMPOSITE: + { + contour_point_vector_t comp_points; + hb_array_t points_left = points.as_array (); + for (auto &item : get_var_composite_iterator ()) + { + hb_array_t record_points = points_left.sub_array (0, item.get_num_points ()); + + comp_points.reset (); + + auto component_coords = coords; + if (item.is_reset_unspecified_axes ()) + component_coords = hb_array (); + + coord_setter_t coord_setter (component_coords); + item.set_variations (coord_setter, record_points); + + if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ()) + .get_points (font, + glyf_accelerator, + comp_points, + deltas, + head_maxp_info, + nullptr, + shift_points_hori, + use_my_metrics, + phantom_only, + coord_setter.get_coords (), + depth + 1, + edge_count))) + return false; + + /* Apply component transformation */ + item.transform_points (record_points, comp_points); + + /* Copy phantom points from component if USE_MY_METRICS flag set */ + if (use_my_metrics && item.is_use_my_metrics ()) + for (unsigned int i = 0; i < PHANTOM_COUNT; i++) + phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; + + all_points.extend (comp_points.as_array ().sub_array (0, comp_points.length - PHANTOM_COUNT)); + + if (all_points.length > HB_GLYF_MAX_POINTS) + return false; + + points_left += item.get_num_points (); + } all_points.extend (phantoms); } break; +#endif default: all_points.extend (phantoms); + break; } - if (depth == 0) /* Apply at top level */ + if (depth == 0 && shift_points_hori) /* Apply at top level */ { /* Undocumented rasterizer behavior: * Shift points horizontally by the updated left side bearing @@ -198,23 +495,30 @@ struct Glyph return !all_points.in_error (); } - bool get_extents (hb_font_t *font, const glyf_accelerator_t &glyf_accelerator, - hb_glyph_extents_t *extents) const + bool get_extents_without_var_scaled (hb_font_t *font, const glyf_accelerator_t &glyf_accelerator, + hb_glyph_extents_t *extents) const { if (type == EMPTY) return true; /* Empty glyph; zero extents. */ - return header->get_extents (font, glyf_accelerator, gid, extents); + return header->get_extents_without_var_scaled (font, glyf_accelerator, gid, extents); } hb_bytes_t get_bytes () const { return bytes; } - Glyph (hb_bytes_t bytes_ = hb_bytes_t (), - hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (bytes_), - header (bytes.as ()), - gid (gid_) + Glyph () : bytes (), + header (bytes.as ()), + gid (-1), + type(EMPTY) + {} + + Glyph (hb_bytes_t bytes_, + hb_codepoint_t gid_ = (unsigned) -1) : bytes (bytes_), + header (bytes.as ()), + gid (gid_) { int num_contours = header->numberOfContours; if (unlikely (num_contours == 0)) type = EMPTY; else if (num_contours > 0) type = SIMPLE; + else if (num_contours == -2) type = VAR_COMPOSITE; else type = COMPOSITE; /* negative numbers */ } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/GlyphHeader.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/GlyphHeader.hh index a2a83fe198a..72c99f88cdd 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/GlyphHeader.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/GlyphHeader.hh @@ -14,15 +14,19 @@ struct GlyphHeader bool has_data () const { return numberOfContours; } template - bool get_extents (hb_font_t *font, const accelerator_t &glyf_accelerator, - hb_codepoint_t gid, hb_glyph_extents_t *extents) const + bool get_extents_without_var_scaled (hb_font_t *font, const accelerator_t &glyf_accelerator, + hb_codepoint_t gid, hb_glyph_extents_t *extents) const { /* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */ /* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */ - extents->x_bearing = font->em_scale_x (glyf_accelerator.hmtx->get_side_bearing (gid)); - extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax)); - extents->width = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax)); - extents->height = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax)); + int lsb = hb_min (xMin, xMax); + (void) glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb); + extents->x_bearing = lsb; + extents->y_bearing = hb_max (yMin, yMax); + extents->width = hb_max (xMin, xMax) - hb_min (xMin, xMax); + extents->height = hb_min (yMin, yMax) - hb_max (yMin, yMax); + + font->scale_glyph_extents (extents); return true; } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh index 4b74967928b..432f368079f 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh @@ -20,7 +20,7 @@ struct SimpleGlyph FLAG_X_SAME = 0x10, FLAG_Y_SAME = 0x20, FLAG_OVERLAP_SIMPLE = 0x40, - FLAG_RESERVED2 = 0x80 + FLAG_CUBIC = 0x80 }; const GlyphHeader &header; @@ -132,8 +132,8 @@ struct SimpleGlyph if (unlikely (p + 1 > end)) return false; unsigned int repeat_count = *p++; unsigned stop = hb_min (i + repeat_count, count); - for (; i < stop;) - points_.arrayZ[i++].flag = flag; + for (; i < stop; i++) + points_.arrayZ[i].flag = flag; } } return true; @@ -184,7 +184,7 @@ struct SimpleGlyph if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours]))) return false; unsigned int num_points = endPtsOfContours[num_contours - 1] + 1; - points_.alloc (num_points + 4); // Allocate for phantom points, to avoid a possible copy + points_.alloc (num_points + 4, true); // Allocate for phantom points, to avoid a possible copy if (!points_.resize (num_points)) return false; if (phantom_only) return true; @@ -206,6 +206,129 @@ struct SimpleGlyph && read_points (p, points_, end, &contour_point_t::y, FLAG_Y_SHORT, FLAG_Y_SAME); } + + static void encode_coord (int value, + uint8_t &flag, + const simple_glyph_flag_t short_flag, + const simple_glyph_flag_t same_flag, + hb_vector_t &coords /* OUT */) + { + if (value == 0) + { + flag |= same_flag; + } + else if (value >= -255 && value <= 255) + { + flag |= short_flag; + if (value > 0) flag |= same_flag; + else value = -value; + + coords.arrayZ[coords.length++] = (uint8_t) value; + } + else + { + int16_t val = value; + coords.arrayZ[coords.length++] = val >> 8; + coords.arrayZ[coords.length++] = val & 0xff; + } + } + + static void encode_flag (uint8_t &flag, + uint8_t &repeat, + uint8_t lastflag, + hb_vector_t &flags /* OUT */) + { + if (flag == lastflag && repeat != 255) + { + repeat++; + if (repeat == 1) + { + /* We know there's room. */ + flags.arrayZ[flags.length++] = flag; + } + else + { + unsigned len = flags.length; + flags.arrayZ[len-2] = flag | FLAG_REPEAT; + flags.arrayZ[len-1] = repeat; + } + } + else + { + repeat = 0; + flags.push (flag); + } + } + + bool compile_bytes_with_deltas (const contour_point_vector_t &all_points, + bool no_hinting, + hb_bytes_t &dest_bytes /* OUT */) + { + if (header.numberOfContours == 0 || all_points.length <= 4) + { + dest_bytes = hb_bytes_t (); + return true; + } + unsigned num_points = all_points.length - 4; + + hb_vector_t flags, x_coords, y_coords; + if (unlikely (!flags.alloc (num_points, true))) return false; + if (unlikely (!x_coords.alloc (2*num_points, true))) return false; + if (unlikely (!y_coords.alloc (2*num_points, true))) return false; + + uint8_t lastflag = 255, repeat = 0; + int prev_x = 0, prev_y = 0; + + for (unsigned i = 0; i < num_points; i++) + { + uint8_t flag = all_points.arrayZ[i].flag; + flag &= FLAG_ON_CURVE + FLAG_OVERLAP_SIMPLE; + + int cur_x = roundf (all_points.arrayZ[i].x); + int cur_y = roundf (all_points.arrayZ[i].y); + encode_coord (cur_x - prev_x, flag, FLAG_X_SHORT, FLAG_X_SAME, x_coords); + encode_coord (cur_y - prev_y, flag, FLAG_Y_SHORT, FLAG_Y_SAME, y_coords); + encode_flag (flag, repeat, lastflag, flags); + + prev_x = cur_x; + prev_y = cur_y; + lastflag = flag; + } + + unsigned len_before_instrs = 2 * header.numberOfContours + 2; + unsigned len_instrs = instructions_length (); + unsigned total_len = len_before_instrs + flags.length + x_coords.length + y_coords.length; + + if (!no_hinting) + total_len += len_instrs; + + char *p = (char *) hb_malloc (total_len); + if (unlikely (!p)) return false; + + const char *src = bytes.arrayZ + GlyphHeader::static_size; + char *cur = p; + hb_memcpy (p, src, len_before_instrs); + + cur += len_before_instrs; + src += len_before_instrs; + + if (!no_hinting) + { + hb_memcpy (cur, src, len_instrs); + cur += len_instrs; + } + + hb_memcpy (cur, flags.arrayZ, flags.length); + cur += flags.length; + + hb_memcpy (cur, x_coords.arrayZ, x_coords.length); + cur += x_coords.length; + + hb_memcpy (cur, y_coords.arrayZ, y_coords.length); + + dest_bytes = hb_bytes_t (p, total_len); + return true; + } }; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/SubsetGlyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/SubsetGlyph.hh index 8106a80338e..9d1c8e22776 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/SubsetGlyph.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/SubsetGlyph.hh @@ -6,12 +6,14 @@ namespace OT { + +struct glyf_accelerator_t; + namespace glyf_impl { struct SubsetGlyph { - hb_codepoint_t new_gid; hb_codepoint_t old_gid; Glyph source_glyph; hb_bytes_t dest_start; /* region of source_glyph to copy first */ @@ -19,14 +21,14 @@ struct SubsetGlyph bool serialize (hb_serialize_context_t *c, bool use_short_loca, - const hb_subset_plan_t *plan) const + const hb_subset_plan_t *plan) { TRACE_SERIALIZE (this); hb_bytes_t dest_glyph = dest_start.copy (c); dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length); unsigned int pad_length = use_short_loca ? padding () : 0; - DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length + pad_length, pad_length); + DEBUG_MSG (SUBSET, nullptr, "serialize %u byte glyph, width %u pad %u", dest_glyph.length, dest_glyph.length + pad_length, pad_length); HBUINT8 pad; pad = 0; @@ -42,8 +44,8 @@ struct SubsetGlyph for (auto &_ : Glyph (dest_glyph).get_composite_iterator ()) { hb_codepoint_t new_gid; - if (plan->new_gid_for_old_gid (_.glyphIndex, &new_gid)) - const_cast (_).glyphIndex = new_gid; + if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid)) + const_cast (_).set_gid (new_gid); } if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) @@ -55,6 +57,17 @@ struct SubsetGlyph return_trace (true); } + bool compile_bytes_with_deltas (const hb_subset_plan_t *plan, + hb_font_t *font, + const glyf_accelerator_t &glyf) + { return source_glyph.compile_bytes_with_deltas (plan, font, glyf, dest_start, dest_end); } + + void free_compiled_bytes () + { + dest_start.fini (); + dest_end.fini (); + } + void drop_hints_bytes () { source_glyph.drop_hints_bytes (dest_start, dest_end); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/VarCompositeGlyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/VarCompositeGlyph.hh new file mode 100644 index 00000000000..2b2d25f05cd --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/VarCompositeGlyph.hh @@ -0,0 +1,354 @@ +#ifndef OT_GLYF_VARCOMPOSITEGLYPH_HH +#define OT_GLYF_VARCOMPOSITEGLYPH_HH + + +#include "../../hb-open-type.hh" +#include "coord-setter.hh" + + +namespace OT { +namespace glyf_impl { + + +struct VarCompositeGlyphRecord +{ + protected: + enum var_composite_glyph_flag_t + { + USE_MY_METRICS = 0x0001, + AXIS_INDICES_ARE_SHORT = 0x0002, + UNIFORM_SCALE = 0x0004, + HAVE_TRANSLATE_X = 0x0008, + HAVE_TRANSLATE_Y = 0x0010, + HAVE_ROTATION = 0x0020, + HAVE_SCALE_X = 0x0040, + HAVE_SCALE_Y = 0x0080, + HAVE_SKEW_X = 0x0100, + HAVE_SKEW_Y = 0x0200, + HAVE_TCENTER_X = 0x0400, + HAVE_TCENTER_Y = 0x0800, + GID_IS_24 = 0x1000, + AXES_HAVE_VARIATION = 0x2000, + RESET_UNSPECIFIED_AXES = 0x4000, + }; + + public: + + unsigned int get_size () const + { + unsigned int size = min_size; + + unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 4 : 3; + size += numAxes * axis_width; + + // gid + size += 2; + if (flags & GID_IS_24) size += 1; + + if (flags & HAVE_TRANSLATE_X) size += 2; + if (flags & HAVE_TRANSLATE_Y) size += 2; + if (flags & HAVE_ROTATION) size += 2; + if (flags & HAVE_SCALE_X) size += 2; + if (flags & HAVE_SCALE_Y) size += 2; + if (flags & HAVE_SKEW_X) size += 2; + if (flags & HAVE_SKEW_Y) size += 2; + if (flags & HAVE_TCENTER_X) size += 2; + if (flags & HAVE_TCENTER_Y) size += 2; + + return size; + } + + bool has_more () const { return true; } + + bool is_use_my_metrics () const { return flags & USE_MY_METRICS; } + bool is_reset_unspecified_axes () const { return flags & RESET_UNSPECIFIED_AXES; } + + hb_codepoint_t get_gid () const + { + if (flags & GID_IS_24) + return StructAfter (numAxes); + else + return StructAfter (numAxes); + } + + unsigned get_numAxes () const + { + return numAxes; + } + + unsigned get_num_points () const + { + unsigned num = 0; + if (flags & AXES_HAVE_VARIATION) num += numAxes; + if (flags & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y)) num++; + if (flags & HAVE_ROTATION) num++; + if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y)) num++; + if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y)) num++; + if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y)) num++; + return num; + } + + void transform_points (hb_array_t record_points, + contour_point_vector_t &points) const + { + float matrix[4]; + contour_point_t trans; + + get_transformation_from_points (record_points, matrix, trans); + + points.transform (matrix); + points.translate (trans); + } + + static inline void transform (float (&matrix)[4], contour_point_t &trans, + float (other)[6]) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L268 + float xx1 = other[0]; + float xy1 = other[1]; + float yx1 = other[2]; + float yy1 = other[3]; + float dx1 = other[4]; + float dy1 = other[5]; + float xx2 = matrix[0]; + float xy2 = matrix[1]; + float yx2 = matrix[2]; + float yy2 = matrix[3]; + float dx2 = trans.x; + float dy2 = trans.y; + + matrix[0] = xx1*xx2 + xy1*yx2; + matrix[1] = xx1*xy2 + xy1*yy2; + matrix[2] = yx1*xx2 + yy1*yx2; + matrix[3] = yx1*xy2 + yy1*yy2; + trans.x = xx2*dx1 + yx2*dy1 + dx2; + trans.y = xy2*dx1 + yy2*dy1 + dy2; + } + + static void translate (float (&matrix)[4], contour_point_t &trans, + float translateX, float translateY) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L213 + float other[6] = {1.f, 0.f, 0.f, 1.f, translateX, translateY}; + transform (matrix, trans, other); + } + + static void scale (float (&matrix)[4], contour_point_t &trans, + float scaleX, float scaleY) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L224 + float other[6] = {scaleX, 0.f, 0.f, scaleY, 0.f, 0.f}; + transform (matrix, trans, other); + } + + static void rotate (float (&matrix)[4], contour_point_t &trans, + float rotation) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240 + rotation = rotation * float (M_PI); + float c = cosf (rotation); + float s = sinf (rotation); + float other[6] = {c, s, -s, c, 0.f, 0.f}; + transform (matrix, trans, other); + } + + static void skew (float (&matrix)[4], contour_point_t &trans, + float skewX, float skewY) + { + // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255 + skewX = skewX * float (M_PI); + skewY = skewY * float (M_PI); + float other[6] = {1.f, tanf (skewY), tanf (skewX), 1.f, 0.f, 0.f}; + transform (matrix, trans, other); + } + + bool get_points (contour_point_vector_t &points) const + { + float translateX = 0.f; + float translateY = 0.f; + float rotation = 0.f; + float scaleX = 1.f * (1 << 10); + float scaleY = 1.f * (1 << 10); + float skewX = 0.f; + float skewY = 0.f; + float tCenterX = 0.f; + float tCenterY = 0.f; + + if (unlikely (!points.resize (points.length + get_num_points ()))) return false; + + unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 2 : 1; + unsigned axes_size = numAxes * axis_width; + + const F2DOT14 *q = (const F2DOT14 *) (axes_size + + (flags & GID_IS_24 ? 3 : 2) + + &StructAfter (numAxes)); + + hb_array_t rec_points = points.as_array ().sub_array (points.length - get_num_points ()); + + unsigned count = numAxes; + if (flags & AXES_HAVE_VARIATION) + { + for (unsigned i = 0; i < count; i++) + rec_points[i].x = q++->to_int (); + rec_points += count; + } + else + q += count; + + const HBUINT16 *p = (const HBUINT16 *) q; + + if (flags & HAVE_TRANSLATE_X) translateX = * (const FWORD *) p++; + if (flags & HAVE_TRANSLATE_Y) translateY = * (const FWORD *) p++; + if (flags & HAVE_ROTATION) rotation = ((const F4DOT12 *) p++)->to_int (); + if (flags & HAVE_SCALE_X) scaleX = ((const F6DOT10 *) p++)->to_int (); + if (flags & HAVE_SCALE_Y) scaleY = ((const F6DOT10 *) p++)->to_int (); + if (flags & HAVE_SKEW_X) skewX = ((const F4DOT12 *) p++)->to_int (); + if (flags & HAVE_SKEW_Y) skewY = ((const F4DOT12 *) p++)->to_int (); + if (flags & HAVE_TCENTER_X) tCenterX = * (const FWORD *) p++; + if (flags & HAVE_TCENTER_Y) tCenterY = * (const FWORD *) p++; + + if ((flags & UNIFORM_SCALE) && !(flags & HAVE_SCALE_Y)) + scaleY = scaleX; + + if (flags & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y)) + { + rec_points[0].x = translateX; + rec_points[0].y = translateY; + rec_points++; + } + if (flags & HAVE_ROTATION) + { + rec_points[0].x = rotation; + rec_points++; + } + if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y)) + { + rec_points[0].x = scaleX; + rec_points[0].y = scaleY; + rec_points++; + } + if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y)) + { + rec_points[0].x = skewX; + rec_points[0].y = skewY; + rec_points++; + } + if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y)) + { + rec_points[0].x = tCenterX; + rec_points[0].y = tCenterY; + rec_points++; + } + assert (!rec_points); + + return true; + } + + void get_transformation_from_points (hb_array_t rec_points, + float (&matrix)[4], contour_point_t &trans) const + { + if (flags & AXES_HAVE_VARIATION) + rec_points += numAxes; + + matrix[0] = matrix[3] = 1.f; + matrix[1] = matrix[2] = 0.f; + trans.init (0.f, 0.f); + + float translateX = 0.f; + float translateY = 0.f; + float rotation = 0.f; + float scaleX = 1.f; + float scaleY = 1.f; + float skewX = 0.f; + float skewY = 0.f; + float tCenterX = 0.f; + float tCenterY = 0.f; + + if (flags & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y)) + { + translateX = rec_points[0].x; + translateY = rec_points[0].y; + rec_points++; + } + if (flags & HAVE_ROTATION) + { + rotation = rec_points[0].x / (1 << 12); + rec_points++; + } + if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y)) + { + scaleX = rec_points[0].x / (1 << 10); + scaleY = rec_points[0].y / (1 << 10); + rec_points++; + } + if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y)) + { + skewX = rec_points[0].x / (1 << 12); + skewY = rec_points[0].y / (1 << 12); + rec_points++; + } + if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y)) + { + tCenterX = rec_points[0].x; + tCenterY = rec_points[0].y; + rec_points++; + } + assert (!rec_points); + + translate (matrix, trans, translateX + tCenterX, translateY + tCenterY); + rotate (matrix, trans, rotation); + scale (matrix, trans, scaleX, scaleY); + skew (matrix, trans, -skewX, skewY); + translate (matrix, trans, -tCenterX, -tCenterY); + } + + void set_variations (coord_setter_t &setter, + hb_array_t rec_points) const + { + bool have_variations = flags & AXES_HAVE_VARIATION; + unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 2 : 1; + + const HBUINT8 *p = (const HBUINT8 *) (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24 ? 3 : 2)); + const HBUINT16 *q = (const HBUINT16 *) (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24 ? 3 : 2)); + + const F2DOT14 *a = (const F2DOT14 *) ((HBUINT8 *) (axis_width == 1 ? (p + numAxes) : (HBUINT8 *) (q + numAxes))); + + unsigned count = numAxes; + for (unsigned i = 0; i < count; i++) + { + unsigned axis_index = axis_width == 1 ? (unsigned) *p++ : (unsigned) *q++; + + signed v = have_variations ? rec_points[i].x : a++->to_int (); + + v = hb_clamp (v, -(1<<14), (1<<14)); + setter[axis_index] = v; + } + } + + protected: + HBUINT16 flags; + HBUINT8 numAxes; + public: + DEFINE_SIZE_MIN (3); +}; + +using var_composite_iter_t = composite_iter_tmpl; + +struct VarCompositeGlyph +{ + const GlyphHeader &header; + hb_bytes_t bytes; + VarCompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + var_composite_iter_t iter () const + { return var_composite_iter_t (bytes, &StructAfter (header)); } + +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_VARCOMPOSITEGLYPH_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/composite-iter.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/composite-iter.hh new file mode 100644 index 00000000000..21e6b4ee962 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/composite-iter.hh @@ -0,0 +1,68 @@ +#ifndef OT_GLYF_COMPOSITE_ITER_HH +#define OT_GLYF_COMPOSITE_ITER_HH + + +#include "../../hb.hh" + + +namespace OT { +namespace glyf_impl { + + +template +struct composite_iter_tmpl : hb_iter_with_fallback_t, + const CompositeGlyphRecord &> +{ + typedef const CompositeGlyphRecord *__item_t__; + composite_iter_tmpl (hb_bytes_t glyph_, __item_t__ current_) : + glyph (glyph_), current (nullptr), current_size (0) + { + set_current (current_); + } + + composite_iter_tmpl () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {} + + const CompositeGlyphRecord & __item__ () const { return *current; } + bool __more__ () const { return current; } + void __next__ () + { + if (!current->has_more ()) { current = nullptr; return; } + + set_current (&StructAtOffset (current, current_size)); + } + composite_iter_tmpl __end__ () const { return composite_iter_tmpl (); } + bool operator != (const composite_iter_tmpl& o) const + { return current != o.current; } + + + void set_current (__item_t__ current_) + { + if (!glyph.check_range (current_, CompositeGlyphRecord::min_size)) + { + current = nullptr; + current_size = 0; + return; + } + unsigned size = current_->get_size (); + if (!glyph.check_range (current_, size)) + { + current = nullptr; + current_size = 0; + return; + } + + current = current_; + current_size = size; + } + + private: + hb_bytes_t glyph; + __item_t__ current; + unsigned current_size; +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + +#endif /* OT_GLYF_COMPOSITE_ITER_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/coord-setter.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/coord-setter.hh new file mode 100644 index 00000000000..df64ed5af7d --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/coord-setter.hh @@ -0,0 +1,34 @@ +#ifndef OT_GLYF_COORD_SETTER_HH +#define OT_GLYF_COORD_SETTER_HH + + +#include "../../hb.hh" + + +namespace OT { +namespace glyf_impl { + + +struct coord_setter_t +{ + coord_setter_t (hb_array_t coords) : + coords (coords) {} + + int& operator [] (unsigned idx) + { + if (coords.length < idx + 1) + coords.resize (idx + 1); + return coords[idx]; + } + + hb_array_t get_coords () + { return coords.as_array (); } + + hb_vector_t coords; +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + +#endif /* OT_GLYF_COORD_SETTER_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf-helpers.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf-helpers.hh index 1fad84ce831..18e2d92d0f4 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf-helpers.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf-helpers.hh @@ -16,7 +16,7 @@ template static void -_write_loca (IteratorIn it, bool short_offsets, IteratorOut dest) +_write_loca (IteratorIn&& it, bool short_offsets, IteratorOut&& dest) { unsigned right_shift = short_offsets ? 1 : 0; unsigned int offset = 0; @@ -25,7 +25,7 @@ _write_loca (IteratorIn it, bool short_offsets, IteratorOut dest) | hb_map ([=, &offset] (unsigned int padded_size) { offset += padded_size; - DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d", offset); + DEBUG_MSG (SUBSET, nullptr, "loca entry offset %u", offset); return offset >> right_shift; }) | hb_sink (dest) @@ -44,6 +44,20 @@ _add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca) head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr); head_prime->indexToLocFormat = use_short_loca ? 0 : 1; + if (plan->normalized_coords) + { + head_prime->xMin = plan->head_maxp_info.xMin; + head_prime->xMax = plan->head_maxp_info.xMax; + head_prime->yMin = plan->head_maxp_info.yMin; + head_prime->yMax = plan->head_maxp_info.yMax; + + unsigned orig_flag = head_prime->flags; + if (plan->head_maxp_info.allXMinIsLsb) + orig_flag |= 1 << 1; + else + orig_flag &= ~(1 << 1); + head_prime->flags = orig_flag; + } bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob); hb_blob_destroy (head_prime_blob); @@ -61,7 +75,7 @@ _add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets, bool use_s if (unlikely (!loca_prime_data)) return false; - DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d size %d", + DEBUG_MSG (SUBSET, nullptr, "loca entry_size %u num_offsets %u size %u", entry_size, num_offsets, entry_size * num_offsets); if (use_short_loca) diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh index caaf2d1f2eb..7a25f07d328 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh @@ -7,6 +7,7 @@ #include "../../hb-ot-hmtx-table.hh" #include "../../hb-ot-var-gvar-table.hh" #include "../../hb-draw.hh" +#include "../../hb-paint.hh" #include "glyf-helpers.hh" #include "Glyph.hh" @@ -24,7 +25,6 @@ namespace OT { */ #define HB_OT_TAG_glyf HB_TAG('g','l','y','f') - struct glyf { friend struct glyf_accelerator_t; @@ -46,8 +46,11 @@ struct glyf const hb_subset_plan_t *plan) { TRACE_SERIALIZE (this); + unsigned init_len = c->length (); - for (const auto &_ : it) _.serialize (c, use_short_loca, plan); + for (auto &_ : it) + if (unlikely (!_.serialize (c, use_short_loca, plan))) + return false; /* As a special case when all glyph in the font are empty, add a zero byte * to the table, so that OTS doesn’t reject it, and to make the table work @@ -72,36 +75,67 @@ struct glyf glyf *glyf_prime = c->serializer->start_embed (); if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false); + hb_font_t *font = nullptr; + if (c->plan->normalized_coords) + { + font = _create_font_for_instancing (c->plan); + if (unlikely (!font)) return false; + } + + hb_vector_t padded_offsets; + unsigned num_glyphs = c->plan->num_output_glyphs (); + if (unlikely (!padded_offsets.resize (num_glyphs))) + return false; + hb_vector_t glyphs; - _populate_subset_glyphs (c->plan, &glyphs); + if (!_populate_subset_glyphs (c->plan, font, glyphs)) + return false; - auto padded_offsets = - + hb_iter (glyphs) - | hb_map (&glyf_impl::SubsetGlyph::padded_size) - ; + if (font) + hb_font_destroy (font); - unsigned max_offset = + padded_offsets | hb_reduce (hb_add, 0); - bool use_short_loca = max_offset < 0x1FFFF; + unsigned max_offset = 0; + for (unsigned i = 0; i < num_glyphs; i++) + { + padded_offsets[i] = glyphs[i].padded_size (); + max_offset += padded_offsets[i]; + } + bool use_short_loca = false; + if (likely (!c->plan->force_long_loca)) + use_short_loca = max_offset < 0x1FFFF; - glyf_prime->serialize (c->serializer, hb_iter (glyphs), use_short_loca, c->plan); if (!use_short_loca) { - padded_offsets = - + hb_iter (glyphs) - | hb_map (&glyf_impl::SubsetGlyph::length) - ; + for (unsigned i = 0; i < num_glyphs; i++) + padded_offsets[i] = glyphs[i].length (); } + bool result = glyf_prime->serialize (c->serializer, glyphs.writer (), use_short_loca, c->plan); + if (c->plan->normalized_coords && !c->plan->pinned_at_default) + _free_compiled_subset_glyphs (glyphs, glyphs.length - 1); + + if (!result) return false; if (unlikely (c->serializer->in_error ())) return_trace (false); + return_trace (c->serializer->check_success (glyf_impl::_add_loca_and_head (c->plan, - padded_offsets, + padded_offsets.iter (), use_short_loca))); } - void + bool _populate_subset_glyphs (const hb_subset_plan_t *plan, - hb_vector_t *glyphs /* OUT */) const; + hb_font_t *font, + hb_vector_t &glyphs /* OUT */) const; + + hb_font_t * + _create_font_for_instancing (const hb_subset_plan_t *plan) const; + + void _free_compiled_subset_glyphs (hb_vector_t &glyphs, unsigned index) const + { + for (unsigned i = 0; i <= index && i < glyphs.length; i++) + glyphs[i].free_compiled_bytes (); + } protected: UnsizedArrayOf @@ -166,7 +200,7 @@ struct glyf_accelerator_t contour_point_vector_t all_points; bool phantom_only = !consumer.is_consuming_contour_points (); - if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, phantom_only))) + if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, nullptr, nullptr, true, true, phantom_only))) return false; if (consumer.is_consuming_contour_points ()) @@ -194,6 +228,7 @@ struct glyf_accelerator_t hb_font_t *font; hb_glyph_extents_t *extents; contour_point_t *phantoms; + bool scaled; struct contour_bounds_t { @@ -209,7 +244,7 @@ struct glyf_accelerator_t bool empty () const { return (min_x >= max_x) || (min_y >= max_y); } - void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scaled) { if (unlikely (empty ())) { @@ -219,26 +254,32 @@ struct glyf_accelerator_t extents->y_bearing = 0; return; } - extents->x_bearing = font->em_scalef_x (min_x); - extents->width = font->em_scalef_x (max_x) - extents->x_bearing; - extents->y_bearing = font->em_scalef_y (max_y); - extents->height = font->em_scalef_y (min_y) - extents->y_bearing; + { + extents->x_bearing = roundf (min_x); + extents->width = roundf (max_x - extents->x_bearing); + extents->y_bearing = roundf (max_y); + extents->height = roundf (min_y - extents->y_bearing); + + if (scaled) + font->scale_glyph_extents (extents); + } } protected: float min_x, min_y, max_x, max_y; } bounds; - points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_) + points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_, bool scaled_) { font = font_; extents = extents_; phantoms = phantoms_; + scaled = scaled_; if (extents) bounds = contour_bounds_t (); } void consume_point (const contour_point_t &point) { bounds.add (point); } - void points_end () { bounds.get_extents (font, extents); } + void points_end () { bounds.get_extents (font, extents, scaled); } bool is_consuming_contour_points () { return extents; } contour_point_t *get_phantoms_sink () { return phantoms; } @@ -246,22 +287,22 @@ struct glyf_accelerator_t public: unsigned - get_advance_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const + get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const { if (unlikely (gid >= num_glyphs)) return 0; bool success = false; contour_point_t phantoms[glyf_impl::PHANTOM_COUNT]; - if (likely (font->num_coords == gvar->get_axis_count ())) - success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms)); + if (font->num_coords) + success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false)); if (unlikely (!success)) return #ifndef HB_NO_VERTICAL - is_vertical ? vmtx->get_advance (gid) : + is_vertical ? vmtx->get_advance_without_var_unscaled (gid) : #endif - hmtx->get_advance (gid); + hmtx->get_advance_without_var_unscaled (gid); float result = is_vertical ? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y @@ -269,23 +310,20 @@ struct glyf_accelerator_t return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2); } - int get_side_bearing_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const + bool get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical, int *lsb) const { - if (unlikely (gid >= num_glyphs)) return 0; + if (unlikely (gid >= num_glyphs)) return false; hb_glyph_extents_t extents; contour_point_t phantoms[glyf_impl::PHANTOM_COUNT]; - if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms)))) - return -#ifndef HB_NO_VERTICAL - is_vertical ? vmtx->get_side_bearing (gid) : -#endif - hmtx->get_side_bearing (gid); + if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false)))) + return false; - return is_vertical - ? ceilf (phantoms[glyf_impl::PHANTOM_TOP].y) - extents.y_bearing - : floorf (phantoms[glyf_impl::PHANTOM_LEFT].x); + *lsb = is_vertical + ? roundf (phantoms[glyf_impl::PHANTOM_TOP].y) - extents.y_bearing + : roundf (phantoms[glyf_impl::PHANTOM_LEFT].x); + return true; } #endif @@ -296,9 +334,18 @@ struct glyf_accelerator_t #ifndef HB_NO_VAR if (font->num_coords) - return get_points (font, gid, points_aggregator_t (font, extents, nullptr)); + return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true)); #endif - return glyph_for_gid (gid).get_extents (font, *this, extents); + return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents); + } + + bool paint_glyph (hb_font_t *font, hb_codepoint_t gid, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const + { + funcs->push_clip_glyph (data, gid, font); + funcs->color (data, true, foreground); + funcs->pop_clip (data); + + return true; } const glyf_impl::Glyph @@ -349,37 +396,76 @@ struct glyf_accelerator_t }; -inline void +inline bool glyf::_populate_subset_glyphs (const hb_subset_plan_t *plan, - hb_vector_t *glyphs /* OUT */) const + hb_font_t *font, + hb_vector_t& glyphs /* OUT */) const { OT::glyf_accelerator_t glyf (plan->source); + unsigned num_glyphs = plan->num_output_glyphs (); + if (!glyphs.resize (num_glyphs)) return false; - + hb_range (plan->num_output_glyphs ()) - | hb_map ([&] (hb_codepoint_t new_gid) - { - glyf_impl::SubsetGlyph subset_glyph = {0}; - subset_glyph.new_gid = new_gid; - - /* should never fail: all old gids should be mapped */ - if (!plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid)) - return subset_glyph; - - if (new_gid == 0 && - !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) - subset_glyph.source_glyph = glyf_impl::Glyph (); - else - subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true); - if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) - subset_glyph.drop_hints_bytes (); - else - subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes (); - return subset_glyph; - }) - | hb_sink (glyphs) - ; + unsigned idx = 0; + for (auto p : plan->glyph_map->iter ()) + { + unsigned new_gid = p.second; + glyf_impl::SubsetGlyph& subset_glyph = glyphs.arrayZ[new_gid]; + subset_glyph.old_gid = p.first; + + if (unlikely (new_gid == 0 && + !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) && + !plan->normalized_coords) + subset_glyph.source_glyph = glyf_impl::Glyph (); + else + { + /* If plan has an accelerator, the preprocessing step already trimmed glyphs. + * Don't trim them again! */ + subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, !plan->accelerator); + } + + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + subset_glyph.drop_hints_bytes (); + else + subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes (); + + if (font) + { + if (unlikely (!subset_glyph.compile_bytes_with_deltas (plan, font, glyf))) + { + // when pinned at default, only bounds are updated, thus no need to free + if (!plan->pinned_at_default && idx > 0) + _free_compiled_subset_glyphs (glyphs, idx - 1); + return false; + } + idx++; + } + } + return true; } +inline hb_font_t * +glyf::_create_font_for_instancing (const hb_subset_plan_t *plan) const +{ + hb_font_t *font = hb_font_create (plan->source); + if (unlikely (font == hb_font_get_empty ())) return nullptr; + + hb_vector_t vars; + if (unlikely (!vars.alloc (plan->user_axes_location.get_population (), true))) + return nullptr; + + for (auto _ : plan->user_axes_location) + { + hb_variation_t var; + var.tag = _.first; + var.value = _.second; + vars.push (var); + } + +#ifndef HB_NO_VAR + hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location.get_population ()); +#endif + return font; +} } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/path-builder.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/path-builder.hh index 853c7ed1eaa..ed410e2b16f 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/path-builder.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/path-builder.hh @@ -26,22 +26,29 @@ struct path_builder_t optional_point_t lerp (optional_point_t p, float t) { return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); } - } first_oncurve, first_offcurve, last_offcurve; + } first_oncurve, first_offcurve, last_offcurve, last_offcurve2; path_builder_t (hb_font_t *font_, hb_draw_session_t &draw_session_) { font = font_; draw_session = &draw_session_; - first_oncurve = first_offcurve = last_offcurve = optional_point_t (); + first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t (); } /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287 See also: * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html - * https://stackoverflow.com/a/20772557 */ + * https://stackoverflow.com/a/20772557 + * + * Cubic support added (incomplete). */ void consume_point (const contour_point_t &point) { bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE; +#ifdef HB_NO_CUBIC_GLYF + bool is_cubic = false; +#else + bool is_cubic = !is_on_curve && (point.flag & glyf_impl::SimpleGlyph::FLAG_CUBIC); +#endif optional_point_t p (font->em_fscalef_x (point.x), font->em_fscalef_y (point.y)); if (!first_oncurve) { @@ -69,16 +76,41 @@ struct path_builder_t { if (is_on_curve) { - draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, - p.x, p.y); + if (last_offcurve2) + { + draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y, + last_offcurve.x, last_offcurve.y, + p.x, p.y); + last_offcurve2 = optional_point_t (); + } + else + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + p.x, p.y); last_offcurve = optional_point_t (); } else { - optional_point_t mid = last_offcurve.lerp (p, .5f); - draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, - mid.x, mid.y); - last_offcurve = p; + if (is_cubic && !last_offcurve2) + { + last_offcurve2 = last_offcurve; + last_offcurve = p; + } + else + { + optional_point_t mid = last_offcurve.lerp (p, .5f); + + if (is_cubic) + { + draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y, + last_offcurve.x, last_offcurve.y, + mid.x, mid.y); + last_offcurve2 = optional_point_t (); + } + else + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + mid.x, mid.y); + last_offcurve = p; + } } } else @@ -105,8 +137,15 @@ struct path_builder_t draw_session->quadratic_to (first_offcurve.x, first_offcurve.y, first_oncurve.x, first_oncurve.y); else if (last_offcurve && first_oncurve) - draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, - first_oncurve.x, first_oncurve.y); + { + if (last_offcurve2) + draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y, + last_offcurve.x, last_offcurve.y, + first_oncurve.x, first_oncurve.y); + else + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + first_oncurve.x, first_oncurve.y); + } else if (first_oncurve) draw_session->line_to (first_oncurve.x, first_oncurve.y); else if (first_offcurve) @@ -117,7 +156,7 @@ struct path_builder_t } /* Getting ready for the next contour */ - first_oncurve = first_offcurve = last_offcurve = optional_point_t (); + first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t (); draw_session->close_path (); } } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/name/name.hh b/src/java.desktop/share/native/libharfbuzz/OT/name/name.hh new file mode 100644 index 00000000000..15ff7a8bdb7 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/OT/name/name.hh @@ -0,0 +1,589 @@ +/* + * Copyright © 2011,2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef OT_NAME_NAME_HH +#define OT_NAME_NAME_HH + +#include "../../hb-open-type.hh" +#include "../../hb-ot-name-language.hh" +#include "../../hb-aat-layout.hh" +#include "../../hb-utf.hh" + + +namespace OT { + +template +inline unsigned int +hb_ot_name_convert_utf (hb_bytes_t bytes, + unsigned int *text_size /* IN/OUT */, + typename out_utf_t::codepoint_t *text /* OUT */) +{ + unsigned int src_len = bytes.length / sizeof (typename in_utf_t::codepoint_t); + const typename in_utf_t::codepoint_t *src = (const typename in_utf_t::codepoint_t *) bytes.arrayZ; + const typename in_utf_t::codepoint_t *src_end = src + src_len; + + typename out_utf_t::codepoint_t *dst = text; + + hb_codepoint_t unicode; + const hb_codepoint_t replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; + + if (text_size && *text_size) + { + (*text_size)--; /* Save room for NUL-termination. */ + const typename out_utf_t::codepoint_t *dst_end = text + *text_size; + + while (src < src_end && dst < dst_end) + { + const typename in_utf_t::codepoint_t *src_next = in_utf_t::next (src, src_end, &unicode, replacement); + typename out_utf_t::codepoint_t *dst_next = out_utf_t::encode (dst, dst_end, unicode); + if (dst_next == dst) + break; /* Out-of-room. */ + + dst = dst_next; + src = src_next; + } + + *text_size = dst - text; + *dst = 0; /* NUL-terminate. */ + } + + /* Accumulate length of rest. */ + unsigned int dst_len = dst - text; + while (src < src_end) + { + src = in_utf_t::next (src, src_end, &unicode, replacement); + dst_len += out_utf_t::encode_len (unicode); + } + return dst_len; +} + +#define entry_score var.u16[0] +#define entry_index var.u16[1] + + +/* + * name -- Naming + * https://docs.microsoft.com/en-us/typography/opentype/spec/name + */ +#define HB_OT_TAG_name HB_TAG('n','a','m','e') + +#define UNSUPPORTED 42 + +struct NameRecord +{ + hb_language_t language (hb_face_t *face) const + { +#ifndef HB_NO_OT_NAME_LANGUAGE + unsigned int p = platformID; + unsigned int l = languageID; + + if (p == 3) + return _hb_ot_name_language_for_ms_code (l); + + if (p == 1) + return _hb_ot_name_language_for_mac_code (l); + +#ifndef HB_NO_OT_NAME_LANGUAGE_AAT + if (p == 0) + return face->table.ltag->get_language (l); +#endif + +#endif + return HB_LANGUAGE_INVALID; + } + + uint16_t score () const + { + /* Same order as in cmap::find_best_subtable(). */ + unsigned int p = platformID; + unsigned int e = encodingID; + + /* 32-bit. */ + if (p == 3 && e == 10) return 0; + if (p == 0 && e == 6) return 1; + if (p == 0 && e == 4) return 2; + + /* 16-bit. */ + if (p == 3 && e == 1) return 3; + if (p == 0 && e == 3) return 4; + if (p == 0 && e == 2) return 5; + if (p == 0 && e == 1) return 6; + if (p == 0 && e == 0) return 7; + + /* Symbol. */ + if (p == 3 && e == 0) return 8; + + /* We treat all Mac Latin names as ASCII only. */ + if (p == 1 && e == 0) return 10; /* 10 is magic number :| */ + + return UNSUPPORTED; + } + + NameRecord* copy (hb_serialize_context_t *c, const void *base +#ifdef HB_EXPERIMENTAL_API + , const hb_hashmap_t *name_table_overrides +#endif + ) const + { + TRACE_SERIALIZE (this); + HB_UNUSED auto snap = c->snapshot (); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); +#ifdef HB_EXPERIMENTAL_API + hb_ot_name_record_ids_t record_ids (platformID, encodingID, languageID, nameID); + hb_bytes_t* name_bytes; + + if (name_table_overrides->has (record_ids, &name_bytes)) { + hb_bytes_t encoded_bytes = *name_bytes; + char *name_str_utf16_be = nullptr; + + if (platformID != 1) + { + unsigned text_size = hb_ot_name_convert_utf (*name_bytes, nullptr, nullptr); + + text_size++; // needs to consider NULL terminator for use in hb_ot_name_convert_utf() + unsigned byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size; + name_str_utf16_be = (char *) hb_calloc (byte_len, 1); + if (!name_str_utf16_be) + { + c->revert (snap); + return_trace (nullptr); + } + hb_ot_name_convert_utf (*name_bytes, &text_size, + (hb_utf16_be_t::codepoint_t *) name_str_utf16_be); + + unsigned encoded_byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size; + if (!encoded_byte_len || !c->check_assign (out->length, encoded_byte_len, HB_SERIALIZE_ERROR_INT_OVERFLOW)) { + c->revert (snap); + hb_free (name_str_utf16_be); + return_trace (nullptr); + } + + encoded_bytes = hb_bytes_t (name_str_utf16_be, encoded_byte_len); + } + else + { + // mac platform, copy the UTF-8 string(all ascii characters) as is + if (!c->check_assign (out->length, encoded_bytes.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) { + c->revert (snap); + return_trace (nullptr); + } + } + + out->offset = 0; + c->push (); + encoded_bytes.copy (c); + c->add_link (out->offset, c->pop_pack (), hb_serialize_context_t::Tail, 0); + hb_free (name_str_utf16_be); + } + else +#endif + { + out->offset.serialize_copy (c, offset, base, 0, hb_serialize_context_t::Tail, length); + } + return_trace (out); + } + + bool isUnicode () const + { + unsigned int p = platformID; + unsigned int e = encodingID; + + return (p == 0 || + (p == 3 && (e == 0 || e == 1 || e == 10))); + } + + static int cmp (const void *pa, const void *pb) + { + const NameRecord *a = (const NameRecord *)pa; + const NameRecord *b = (const NameRecord *)pb; + + if (a->platformID != b->platformID) + return a->platformID - b->platformID; + + if (a->encodingID != b->encodingID) + return a->encodingID - b->encodingID; + + if (a->languageID != b->languageID) + return a->languageID - b->languageID; + + if (a->nameID != b->nameID) + return a->nameID - b->nameID; + + if (a->length != b->length) + return a->length - b->length; + + return 0; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && offset.sanitize (c, base, length)); + } + + HBUINT16 platformID; /* Platform ID. */ + HBUINT16 encodingID; /* Platform-specific encoding ID. */ + HBUINT16 languageID; /* Language ID. */ + HBUINT16 nameID; /* Name ID. */ + HBUINT16 length; /* String length (in bytes). */ + NNOffset16To> + offset; /* String offset from start of storage area (in bytes). */ + public: + DEFINE_SIZE_STATIC (12); +}; + +static int +_hb_ot_name_entry_cmp_key (const void *pa, const void *pb, bool exact) +{ + const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa; + const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb; + + /* Compare by name_id, then language. */ + + if (a->name_id != b->name_id) + return a->name_id - b->name_id; + + if (a->language == b->language) return 0; + if (!a->language) return -1; + if (!b->language) return +1; + + const char *astr = hb_language_to_string (a->language); + const char *bstr = hb_language_to_string (b->language); + + signed c = strcmp (astr, bstr); + + // 'a' is the user request, and 'b' is string in the font. + // If eg. user asks for "en-us" and font has "en", approve. + if (!exact && c && + hb_language_matches (b->language, a->language)) + return 0; + + return c; +} + +static int +_hb_ot_name_entry_cmp (const void *pa, const void *pb) +{ + /* Compare by name_id, then language, then score, then index. */ + + int v = _hb_ot_name_entry_cmp_key (pa, pb, true); + if (v) + return v; + + const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa; + const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb; + + if (a->entry_score != b->entry_score) + return a->entry_score - b->entry_score; + + if (a->entry_index != b->entry_index) + return a->entry_index - b->entry_index; + + return 0; +} + +struct name +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_name; + + unsigned int get_size () const + { return min_size + count * nameRecordZ.item_size; } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + const void *src_string_pool +#ifdef HB_EXPERIMENTAL_API + , const hb_vector_t& insert_name_records + , const hb_hashmap_t *name_table_overrides +#endif + ) + { + TRACE_SERIALIZE (this); + + if (unlikely (!c->extend_min ((*this)))) return_trace (false); + + unsigned total_count = it.len () +#ifdef HB_EXPERIMENTAL_API + + insert_name_records.length +#endif + ; + this->format = 0; + if (!c->check_assign (this->count, total_count, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return false; + + NameRecord *name_records = (NameRecord *) hb_calloc (total_count, NameRecord::static_size); + if (unlikely (!name_records)) return_trace (false); + + hb_array_t records (name_records, total_count); + + for (const NameRecord& record : it) + { + hb_memcpy (name_records, &record, NameRecord::static_size); + name_records++; + } + +#ifdef HB_EXPERIMENTAL_API + for (unsigned i = 0; i < insert_name_records.length; i++) + { + const hb_ot_name_record_ids_t& ids = insert_name_records[i]; + NameRecord record; + record.platformID = ids.platform_id; + record.encodingID = ids.encoding_id; + record.languageID = ids.language_id; + record.nameID = ids.name_id; + record.length = 0; // handled in NameRecord copy() + record.offset = 0; + memcpy (name_records, &record, NameRecord::static_size); + name_records++; + } +#endif + + records.qsort (); + + c->copy_all (records, + src_string_pool +#ifdef HB_EXPERIMENTAL_API + , name_table_overrides +#endif + ); + hb_free (records.arrayZ); + + + if (unlikely (c->ran_out_of_room ())) return_trace (false); + + this->stringOffset = c->length (); + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + name *name_prime = c->serializer->start_embed (); + if (unlikely (!name_prime)) return_trace (false); + +#ifdef HB_EXPERIMENTAL_API + const hb_hashmap_t *name_table_overrides = + &c->plan->name_table_overrides; +#endif + + auto it = + + nameRecordZ.as_array (count) + | hb_filter (c->plan->name_ids, &NameRecord::nameID) + | hb_filter (c->plan->name_languages, &NameRecord::languageID) + | hb_filter ([&] (const NameRecord& namerecord) { + return + (c->plan->flags & HB_SUBSET_FLAGS_NAME_LEGACY) + || namerecord.isUnicode (); + }) +#ifdef HB_EXPERIMENTAL_API + | hb_filter ([&] (const NameRecord& namerecord) { + if (name_table_overrides->is_empty ()) + return true; + hb_ot_name_record_ids_t rec_ids (namerecord.platformID, + namerecord.encodingID, + namerecord.languageID, + namerecord.nameID); + + hb_bytes_t *p; + if (name_table_overrides->has (rec_ids, &p) && + (*p).length == 0) + return false; + return true; + }) +#endif + ; + +#ifdef HB_EXPERIMENTAL_API + hb_hashmap_t retained_name_record_ids; + for (const NameRecord& rec : it) + { + hb_ot_name_record_ids_t rec_ids (rec.platformID, + rec.encodingID, + rec.languageID, + rec.nameID); + retained_name_record_ids.set (rec_ids, 1); + } + + hb_vector_t insert_name_records; + if (!name_table_overrides->is_empty ()) + { + if (unlikely (!insert_name_records.alloc (name_table_overrides->get_population (), true))) + return_trace (false); + for (const auto& record_ids : name_table_overrides->keys ()) + { + if (name_table_overrides->get (record_ids).length == 0) + continue; + if (retained_name_record_ids.has (record_ids)) + continue; + insert_name_records.push (record_ids); + } + } +#endif + + return (name_prime->serialize (c->serializer, it, + std::addressof (this + stringOffset) +#ifdef HB_EXPERIMENTAL_API + , insert_name_records + , name_table_overrides +#endif + )); + } + + bool sanitize_records (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + const void *string_pool = (this+stringOffset).arrayZ; + return_trace (nameRecordZ.sanitize (c, count, string_pool)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (format == 0 || format == 1) && + c->check_array (nameRecordZ.arrayZ, count) && + c->check_range (this, stringOffset) && + sanitize_records (c)); + } + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + this->table = hb_sanitize_context_t ().reference_table (face); + assert (this->table.get_length () >= this->table->stringOffset); + this->pool = (const char *) (const void *) (this->table+this->table->stringOffset); + this->pool_len = this->table.get_length () - this->table->stringOffset; + const hb_array_t all_names (this->table->nameRecordZ.arrayZ, + this->table->count); + + this->names.alloc (all_names.length, true); + + for (unsigned int i = 0; i < all_names.length; i++) + { + hb_ot_name_entry_t *entry = this->names.push (); + + entry->name_id = all_names[i].nameID; + entry->language = all_names[i].language (face); + entry->entry_score = all_names[i].score (); + entry->entry_index = i; + } + + this->names.qsort (_hb_ot_name_entry_cmp); + /* Walk and pick best only for each name_id,language pair, + * while dropping unsupported encodings. */ + unsigned int j = 0; + for (unsigned int i = 0; i < this->names.length; i++) + { + if (this->names[i].entry_score == UNSUPPORTED || + this->names[i].language == HB_LANGUAGE_INVALID) + continue; + if (i && + this->names[i - 1].name_id == this->names[i].name_id && + this->names[i - 1].language == this->names[i].language) + continue; + this->names[j++] = this->names[i]; + } + this->names.resize (j); + } + ~accelerator_t () + { + this->table.destroy (); + } + + int get_index (hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *width=nullptr) const + { + const hb_ot_name_entry_t key = {name_id, {0}, language}; + const hb_ot_name_entry_t *entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names, + this->names.length, + sizeof (hb_ot_name_entry_t), + _hb_ot_name_entry_cmp_key, + true); + + if (!entry) + { + entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names, + this->names.length, + sizeof (hb_ot_name_entry_t), + _hb_ot_name_entry_cmp_key, + false); + } + + if (!entry) + return -1; + + if (width) + *width = entry->entry_score < 10 ? 2 : 1; + + return entry->entry_index; + } + + hb_bytes_t get_name (unsigned int idx) const + { + const hb_array_t all_names (table->nameRecordZ.arrayZ, table->count); + const NameRecord &record = all_names[idx]; + const hb_bytes_t string_pool (pool, pool_len); + return string_pool.sub_array (record.offset, record.length); + } + + private: + const char *pool; + unsigned int pool_len; + public: + hb_blob_ptr_t table; + hb_vector_t names; + }; + + public: + /* We only implement format 0 for now. */ + HBUINT16 format; /* Format selector (=0/1). */ + HBUINT16 count; /* Number of name records. */ + NNOffset16To> + stringOffset; /* Offset to start of string storage (from start of table). */ + UnsizedArrayOf + nameRecordZ; /* The name records where count is the number of records. */ + public: + DEFINE_SIZE_ARRAY (6, nameRecordZ); +}; + +#undef entry_index +#undef entry_score + +struct name_accelerator_t : name::accelerator_t { + name_accelerator_t (hb_face_t *face) : name::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* OT_NAME_NAME_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/UPDATING.txt b/src/java.desktop/share/native/libharfbuzz/UPDATING.txt index 34434ddef04..8beacde4b61 100644 --- a/src/java.desktop/share/native/libharfbuzz/UPDATING.txt +++ b/src/java.desktop/share/native/libharfbuzz/UPDATING.txt @@ -1,53 +1,118 @@ Tips and tasks when updating harfbuzz sources to a newer version. ----------------------------------------------------------------- +STEP 1: UPDATING FILES +---------------------- +Download and unzip the latest version from https://github.com/harfbuzz/harfbuzz/releases We only use files from the src directory and even then only the ones we need. So just C++ include and source files and only the ones needed for the library, -and even then just the ones we use. Do NOT just copy everything. +and even then just the ones we use. +IMPORTANT! DO NOT just copy everything. -So one way to update is to - -- copy over from the updated harfbuzz the exact same files we already have -- it isn't a flat directory so watch out for that -- any that are no longer available (copy fails) we remove but these may come - back later if they were actually renamed -- look for files in the destination that were NOT updated - perhaps they - are gone in the upstream - or renamed. Remove them if they are really - obsolete, or add their replacements/renames. -- iterate over : build and see what new file is missing that causes a build failure -- when this is done we have something buildable -- make sure it builds on all supported platforms. -- Harfbuzz is not modular so it is not easy, +- Harfbuzz is not modular, so the update is not a straightforward process. - The main thing is we do NOT want any * "test" programs (test in the name, have a main are clues) * support for (eg) GLib, DirectWrite, Graphite, GDI, ICU, Uniscribe * aggregators like harfbuzz.cc - since it includes things from the above as well as hb-ft.cc which we specifically exclude in the Makefile - * but we do use core text support on macOS. - * I really wish that "src" were just library source but I expect the authors + * but we do use core text support on macOS. + * I really wish that "src" were just library source, but I expect the authors have their reasons. -- we do not apply any header file changes so this is not an issue +So one way to update is to + +- copy over from the updated harfbuzz the exact same files we already have +- it isn't a flat directory so watch out for that. + +- For files that are no longer available (for which copy fails), we remove such files, + but these may come back later if they were actually renamed. + +- look for files in the destination that were NOT updated - perhaps they + are removed or renamed in the upstream. Remove them if they are really + obsolete, or add their replacements/renames. + In IntelliJ IDE: + Newly added files are shown in RED + Modified in BLUE + NOT Updated in WHITE + This feature might be helpful to keep track of new, modified and unchanged files. + + +STEP 2: BUILD CHANGES INCREMENTALLY +----------------------------------- +- iterate over : build and see what new file is missing that causes a build failure. + Sometimes just running a build does not show up any failures due to stale files. + Clean followed by build would be helpful in this situation. + +- You might run into compiler warnings that are treated as errors or the requirement + to set certain compiler flags if the build fails on a specific platform. + Check "COMPILER WARNINGS AND SETTING FLAGS" section for more details. + +- when this is done we have something buildable, make sure it builds + on all supported platforms. + + +STEP 3: COMPILER WARNINGS AND SETTING FLAGS +------------------------------------------- +- Update make parameters in Awt2DLibraries.gmk + Since we don't use configure we need to manually specify the options + we need in the Harfbuzz section of Awt2DLibraries.gmk. + As well as adding new options, we may need to clean up obsolete options. + Note there may be platform variations in the flags. + +- As with other 3rd party libs we do not fix the code to eliminate compiler + warnings unless they are critical and clearly avoiding a bug. Even then + we'd report it upstream and apply the patch once it is made available. + The usual practice is do just disable the warnings. + + +STEP 4: UPDATING .md FILE +------------------------- +- we do not apply any header file changes so this is not an issue. + - verify the license text is unchanged (extra steps are needed if it is) and update - src/java.desktop/share/legal/harfbuzz.md with the new version + src/java.desktop/share/legal/harfbuzz.md with the new version. + + +STEP 5: REPLACE TABS & REMOVE TRAILING SPACES +--------------------------------------------- - clean up trailing white space and tabs to follow jcheck rules. Use "expand" and "sed" to remove tabs and trailing white space from the imported sources. -- test using all the automated jtreg tests on all platforms -- do manual verification of Arabic, Hebrew, Thai, Indic against previous releases. - Look for manual related layout jtreg tests and run on Windows,Linux and Mac. + To clean up the extra spaces and tabs run the following script at + each folder level within libharfbuzz. + + for f in *.c *.h *.cc *.hh; + do + # replace tabs with spaces + expand ${f} > ${f}.tmp; + mv ${f}.tmp $f; + + # fix line endings to LF + sed -e 's/\r$//g' ${f} > ${f}.tmp; + mv ${f}.tmp $f; + + # remove trailing spaces + sed -e 's/[ ]* $//g' ${f} > ${f}.tmp; + mv ${f}.tmp $f; + done + + +STEP 6: TESTING +--------------- +- test using all the automated jtreg tests on all platforms. + +- do MANUAL verification of Arabic, Hebrew, Thai, Indic against previous releases. + Look for manual related layout jtreg tests (test/jdk/java/awt/font/TextLayout) + and run on Windows,Linux and Mac. Use Font2DTest set to TextLayout and check the above languages. Probably not going to see layout problems a code point at a time but it needs to be checked. -- Update make parameters as needed - Since we don't use configure we need to manually specify the options - we need in the harfbuzz section of Awt2DLibraries.gmk. - As well as adding new options, we may need to clean up obsolete options. - Note there may be platform variations in the flags. + Different unicode combinations can be checked using Font2DTest. + Run Font2DTest, select 'UserText' option for 'Text to use'. + Paste unicodes of different languages (Arabic, Hebrew, Thai, Indic) + and compare the glyphs with previous versions. + It should look the same in both cases. -- As with other 3rd party libs we do not fix the code to eliminate compiler - warnings unless they are critical and clearly avoiding a bug. Even then - we'd report it upstream. The usual practice is do just disable the warnings -- Update THIS UPDATING.txt file too if it is outdated. +- FINALLY, Do update THIS UPDATING.txt file too if it is outdated. diff --git a/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh new file mode 100644 index 00000000000..c2e24a70678 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh @@ -0,0 +1,216 @@ +/* + * Copyright © 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#include "graph.hh" +#include "../hb-ot-layout-common.hh" + +#ifndef GRAPH_CLASSDEF_GRAPH_HH +#define GRAPH_CLASSDEF_GRAPH_HH + +namespace graph { + +struct ClassDefFormat1 : public OT::ClassDefFormat1_3 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::ClassDefFormat1_3::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + classValue.get_size () - classValue.len.get_size (); + } +}; + +struct ClassDefFormat2 : public OT::ClassDefFormat2_4 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::ClassDefFormat2_4::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size (); + } +}; + +struct ClassDef : public OT::ClassDef +{ + template + static bool add_class_def (gsubgpos_graph_context_t& c, + unsigned parent_id, + unsigned link_position, + It glyph_and_class, + unsigned max_size) + { + unsigned class_def_prime_id = c.graph.new_node (nullptr, nullptr); + auto& class_def_prime_vertex = c.graph.vertices_[class_def_prime_id]; + if (!make_class_def (c, glyph_and_class, class_def_prime_id, max_size)) + return false; + + auto* class_def_link = c.graph.vertices_[parent_id].obj.real_links.push (); + class_def_link->width = SmallTypes::size; + class_def_link->objidx = class_def_prime_id; + class_def_link->position = link_position; + class_def_prime_vertex.parents.push (parent_id); + + return true; + } + + template + static bool make_class_def (gsubgpos_graph_context_t& c, + It glyph_and_class, + unsigned dest_obj, + unsigned max_size) + { + char* buffer = (char*) hb_calloc (1, max_size); + hb_serialize_context_t serializer (buffer, max_size); + OT::ClassDef_serialize (&serializer, glyph_and_class); + serializer.end_serialize (); + if (serializer.in_error ()) + { + hb_free (buffer); + return false; + } + + hb_bytes_t class_def_copy = serializer.copy_bytes (); + c.add_buffer ((char *) class_def_copy.arrayZ); // Give ownership to the context, it will cleanup the buffer. + + auto& obj = c.graph.vertices_[dest_obj].obj; + obj.head = (char *) class_def_copy.arrayZ; + obj.tail = obj.head + class_def_copy.length; + + hb_free (buffer); + return true; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::ClassDef::min_size) return false; + switch (u.format) + { + case 1: return ((ClassDefFormat1*)this)->sanitize (vertex); + case 2: return ((ClassDefFormat2*)this)->sanitize (vertex); +#ifndef HB_NO_BEYOND_64K + // Not currently supported + case 3: + case 4: +#endif + default: return false; + } + } +}; + + +struct class_def_size_estimator_t +{ + template + class_def_size_estimator_t (It glyph_and_class) + : gids_consecutive (true), num_ranges_per_class (), glyphs_per_class () + { + unsigned last_gid = (unsigned) -1; + for (auto p : + glyph_and_class) + { + unsigned gid = p.first; + unsigned klass = p.second; + + if (last_gid != (unsigned) -1 && gid != last_gid + 1) + gids_consecutive = false; + last_gid = gid; + + hb_set_t* glyphs; + if (glyphs_per_class.has (klass, &glyphs) && glyphs) { + glyphs->add (gid); + continue; + } + + hb_set_t new_glyphs; + new_glyphs.add (gid); + glyphs_per_class.set (klass, std::move (new_glyphs)); + } + + if (in_error ()) return; + + for (unsigned klass : glyphs_per_class.keys ()) + { + if (!klass) continue; // class 0 doesn't get encoded. + + const hb_set_t& glyphs = glyphs_per_class.get (klass); + hb_codepoint_t start = HB_SET_VALUE_INVALID; + hb_codepoint_t end = HB_SET_VALUE_INVALID; + + unsigned count = 0; + while (glyphs.next_range (&start, &end)) + count++; + + num_ranges_per_class.set (klass, count); + } + } + + // Incremental increase in the Coverage and ClassDef table size + // (worst case) if all glyphs associated with 'klass' were added. + unsigned incremental_coverage_size (unsigned klass) const + { + // Coverage takes 2 bytes per glyph worst case, + return 2 * glyphs_per_class.get (klass).get_population (); + } + + // Incremental increase in the Coverage and ClassDef table size + // (worst case) if all glyphs associated with 'klass' were added. + unsigned incremental_class_def_size (unsigned klass) const + { + // ClassDef takes 6 bytes per range + unsigned class_def_2_size = 6 * num_ranges_per_class.get (klass); + if (gids_consecutive) + { + // ClassDef1 takes 2 bytes per glyph, but only can be used + // when gids are consecutive. + return hb_min (2 * glyphs_per_class.get (klass).get_population (), class_def_2_size); + } + + return class_def_2_size; + } + + bool in_error () + { + if (num_ranges_per_class.in_error ()) return true; + if (glyphs_per_class.in_error ()) return true; + + for (const hb_set_t& s : glyphs_per_class.values ()) + { + if (s.in_error ()) return true; + } + return false; + } + + private: + bool gids_consecutive; + hb_hashmap_t num_ranges_per_class; + hb_hashmap_t glyphs_per_class; +}; + + +} + +#endif // GRAPH_CLASSDEF_GRAPH_HH diff --git a/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh new file mode 100644 index 00000000000..49d09363156 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh @@ -0,0 +1,152 @@ +/* + * Copyright © 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#include "graph.hh" +#include "../OT/Layout/Common/Coverage.hh" + +#ifndef GRAPH_COVERAGE_GRAPH_HH +#define GRAPH_COVERAGE_GRAPH_HH + +namespace graph { + +struct CoverageFormat1 : public OT::Layout::Common::CoverageFormat1_3 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::Layout::Common::CoverageFormat1_3::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + glyphArray.get_size () - glyphArray.len.get_size (); + } +}; + +struct CoverageFormat2 : public OT::Layout::Common::CoverageFormat2_4 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::Layout::Common::CoverageFormat2_4::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size (); + } +}; + +struct Coverage : public OT::Layout::Common::Coverage +{ + static Coverage* clone_coverage (gsubgpos_graph_context_t& c, + unsigned coverage_id, + unsigned new_parent_id, + unsigned link_position, + unsigned start, unsigned end) + + { + unsigned coverage_size = c.graph.vertices_[coverage_id].table_size (); + auto& coverage_v = c.graph.vertices_[coverage_id]; + Coverage* coverage_table = (Coverage*) coverage_v.obj.head; + if (!coverage_table || !coverage_table->sanitize (coverage_v)) + return nullptr; + + auto new_coverage = + + hb_zip (coverage_table->iter (), hb_range ()) + | hb_filter ([&] (hb_pair_t p) { + return p.second >= start && p.second < end; + }) + | hb_map_retains_sorting (hb_first) + ; + + return add_coverage (c, new_parent_id, link_position, new_coverage, coverage_size); + } + + template + static Coverage* add_coverage (gsubgpos_graph_context_t& c, + unsigned parent_id, + unsigned link_position, + It glyphs, + unsigned max_size) + { + unsigned coverage_prime_id = c.graph.new_node (nullptr, nullptr); + auto& coverage_prime_vertex = c.graph.vertices_[coverage_prime_id]; + if (!make_coverage (c, glyphs, coverage_prime_id, max_size)) + return nullptr; + + auto* coverage_link = c.graph.vertices_[parent_id].obj.real_links.push (); + coverage_link->width = SmallTypes::size; + coverage_link->objidx = coverage_prime_id; + coverage_link->position = link_position; + coverage_prime_vertex.parents.push (parent_id); + + return (Coverage*) coverage_prime_vertex.obj.head; + } + + template + static bool make_coverage (gsubgpos_graph_context_t& c, + It glyphs, + unsigned dest_obj, + unsigned max_size) + { + char* buffer = (char*) hb_calloc (1, max_size); + hb_serialize_context_t serializer (buffer, max_size); + OT::Layout::Common::Coverage_serialize (&serializer, glyphs); + serializer.end_serialize (); + if (serializer.in_error ()) + { + hb_free (buffer); + return false; + } + + hb_bytes_t coverage_copy = serializer.copy_bytes (); + c.add_buffer ((char *) coverage_copy.arrayZ); // Give ownership to the context, it will cleanup the buffer. + + auto& obj = c.graph.vertices_[dest_obj].obj; + obj.head = (char *) coverage_copy.arrayZ; + obj.tail = obj.head + coverage_copy.length; + + hb_free (buffer); + return true; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::Layout::Common::Coverage::min_size) return false; + switch (u.format) + { + case 1: return ((CoverageFormat1*)this)->sanitize (vertex); + case 2: return ((CoverageFormat2*)this)->sanitize (vertex); +#ifndef HB_NO_BEYOND_64K + // Not currently supported + case 3: + case 4: +#endif + default: return false; + } + } +}; + + +} + +#endif // GRAPH_COVERAGE_GRAPH_HH diff --git a/src/java.desktop/share/native/libharfbuzz/graph/graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/graph.hh index 52ca9dd142e..38ca5db0961 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/graph.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/graph.hh @@ -24,6 +24,10 @@ * Google Author(s): Garret Rieger */ +#include "../hb-set.hh" +#include "../hb-priority-queue.hh" +#include "../hb-serialize.hh" + #ifndef GRAPH_GRAPH_HH #define GRAPH_GRAPH_HH @@ -45,6 +49,95 @@ struct graph_t unsigned end = 0; unsigned priority = 0; + + bool link_positions_valid (unsigned num_objects, bool removed_nil) + { + hb_set_t assigned_bytes; + for (const auto& l : obj.real_links) + { + if (l.objidx >= num_objects + || (removed_nil && !l.objidx)) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Invalid object index."); + return false; + } + + unsigned start = l.position; + unsigned end = start + l.width - 1; + + if (unlikely (l.width < 2 || l.width > 4)) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Invalid link width."); + return false; + } + + if (unlikely (end >= table_size ())) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Link position is out of bounds."); + return false; + } + + if (unlikely (assigned_bytes.intersects (start, end))) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Invalid graph. Found offsets whose positions overlap."); + return false; + } + + assigned_bytes.add_range (start, end); + } + + return !assigned_bytes.in_error (); + } + + void normalize () + { + obj.real_links.qsort (); + for (auto& l : obj.real_links) + { + for (unsigned i = 0; i < l.width; i++) + { + obj.head[l.position + i] = 0; + } + } + } + + bool equals (const vertex_t& other, + const graph_t& graph, + const graph_t& other_graph, + unsigned depth) const + { + if (!(as_bytes () == other.as_bytes ())) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "vertex [%lu] bytes != [%lu] bytes, depth = %u", + (unsigned long) table_size (), + (unsigned long) other.table_size (), + depth); + + auto a = as_bytes (); + auto b = other.as_bytes (); + while (a || b) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " 0x%x %s 0x%x", (unsigned) *a, (*a == *b) ? "==" : "!=", (unsigned) *b); + a++; + b++; + } + return false; + } + + return links_equal (obj.real_links, other.obj.real_links, graph, other_graph, depth); + } + + hb_bytes_t as_bytes () const + { + return hb_bytes_t (obj.head, table_size ()); + } + friend void swap (vertex_t& a, vertex_t& b) { hb_swap (a.obj, b.obj); @@ -56,6 +149,18 @@ struct graph_t hb_swap (a.priority, b.priority); } + hb_hashmap_t + position_to_index_map () const + { + hb_hashmap_t result; + + for (const auto& l : obj.real_links) { + result.set (l.position, l.objidx); + } + + return result; + } + bool is_shared () const { return parents.length > 1; @@ -71,11 +176,27 @@ struct graph_t for (unsigned i = 0; i < parents.length; i++) { if (parents[i] != parent_index) continue; - parents.remove (i); + parents.remove_unordered (i); break; } } + void remove_real_link (unsigned child_index, const void* offset) + { + for (unsigned i = 0; i < obj.real_links.length; i++) + { + auto& link = obj.real_links.arrayZ[i]; + if (link.objidx != child_index) + continue; + + if ((obj.head + link.position) != offset) + continue; + + obj.real_links.remove_unordered (i); + return; + } + } + void remap_parents (const hb_vector_t& id_map) { for (unsigned i = 0; i < parents.length; i++) @@ -107,6 +228,10 @@ struct graph_t return priority >= 3; } + size_t table_size () const { + return obj.tail - obj.head; + } + int64_t modified_distance (unsigned order) const { // TODO(garretrieger): once priority is high enough, should try @@ -131,6 +256,57 @@ struct graph_t return -table_size; } + + private: + bool links_equal (const hb_vector_t& this_links, + const hb_vector_t& other_links, + const graph_t& graph, + const graph_t& other_graph, + unsigned depth) const + { + auto a = this_links.iter (); + auto b = other_links.iter (); + + while (a && b) + { + const auto& link_a = *a; + const auto& link_b = *b; + + if (link_a.width != link_b.width || + link_a.is_signed != link_b.is_signed || + link_a.whence != link_b.whence || + link_a.position != link_b.position || + link_a.bias != link_b.bias) + return false; + + if (!graph.vertices_[link_a.objidx].equals ( + other_graph.vertices_[link_b.objidx], graph, other_graph, depth + 1)) + return false; + + a++; + b++; + } + + if (bool (a) != bool (b)) + return false; + + return true; + } + }; + + template + struct vertex_and_table_t + { + vertex_and_table_t () : index (0), vertex (nullptr), table (nullptr) + {} + + unsigned index; + vertex_t* vertex; + T* table; + + operator bool () { + return table && vertex; + } }; /* @@ -145,7 +321,8 @@ struct graph_t : parents_invalid (true), distance_invalid (true), positions_invalid (true), - successful (true) + successful (true), + buffers () { num_roots_for_space_.push (1); bool removed_nil = false; @@ -153,8 +330,6 @@ struct graph_t vertices_scratch_.alloc (objects.length); for (unsigned i = 0; i < objects.length; i++) { - // TODO(grieger): check all links point to valid objects. - // If this graph came from a serialization buffer object 0 is the // nil object. We don't need it for our purposes here so drop it. if (i == 0 && !objects[i]) @@ -166,6 +341,9 @@ struct graph_t vertex_t* v = vertices_.push (); if (check_success (!vertices_.in_error ())) v->obj = *objects[i]; + + check_success (v->link_positions_valid (objects.length, removed_nil)); + if (!removed_nil) continue; // Fix indices to account for removed nil object. for (auto& l : v->obj.all_links_writer ()) { @@ -177,6 +355,20 @@ struct graph_t ~graph_t () { vertices_.fini (); + for (char* b : buffers) + hb_free (b); + } + + bool operator== (const graph_t& other) const + { + return root ().equals (other.root (), *this, other, 0); + } + + // Sorts links of all objects in a consistent manner and zeroes all offsets. + void normalize () + { + for (auto& v : vertices_.writer ()) + v.normalize (); } bool in_error () const @@ -199,11 +391,43 @@ struct graph_t return vertices_.length - 1; } - const hb_serialize_context_t::object_t& object(unsigned i) const + const hb_serialize_context_t::object_t& object (unsigned i) const { return vertices_[i].obj; } + void add_buffer (char* buffer) + { + buffers.push (buffer); + } + + /* + * Adds a 16 bit link from parent_id to child_id + */ + template + void add_link (T* offset, + unsigned parent_id, + unsigned child_id) + { + auto& v = vertices_[parent_id]; + auto* link = v.obj.real_links.push (); + link->width = 2; + link->objidx = child_id; + link->position = (char*) offset - (char*) v.obj.head; + vertices_[child_id].parents.push (parent_id); + } + + /* + * Generates a new topological sorting of graph ordered by the shortest + * distance to each node if positions are marked as invalid. + */ + void sort_shortest_distance_if_needed () + { + if (!positions_invalid) return; + sort_shortest_distance (); + } + + /* * Generates a new topological sorting of graph ordered by the shortest * distance to each node. @@ -239,6 +463,13 @@ struct graph_t hb_swap (sorted_graph[new_id], vertices_[next_id]); const vertex_t& next = sorted_graph[new_id]; + if (unlikely (!check_success(new_id >= 0))) { + // We are out of ids. Which means we've visited a node more than once. + // This graph contains a cycle which is not allowed. + DEBUG_MSG (SUBSET_REPACK, nullptr, "Invalid graph. Contains cycle."); + return; + } + id_map[next_id] = new_id--; for (const auto& link : next.obj.all_links ()) { @@ -256,44 +487,152 @@ struct graph_t check_success (!queue.in_error ()); check_success (!sorted_graph.in_error ()); - if (!check_success (new_id == -1)) - print_orphaned_nodes (); remap_all_obj_indices (id_map, &sorted_graph); - hb_swap (vertices_, sorted_graph); + + if (!check_success (new_id == -1)) + print_orphaned_nodes (); } /* - * Assign unique space numbers to each connected subgraph of 32 bit offset(s). + * Finds the set of nodes (placed into roots) that should be assigned unique spaces. + * More specifically this looks for the top most 24 bit or 32 bit links in the graph. + * Some special casing is done that is specific to the layout of GSUB/GPOS tables. */ - bool assign_32bit_spaces () + void find_space_roots (hb_set_t& visited, hb_set_t& roots) { - unsigned root_index = root_idx (); - hb_set_t visited; - hb_set_t roots; - for (unsigned i = 0; i <= root_index; i++) + int root_index = (int) root_idx (); + for (int i = root_index; i >= 0; i--) { + if (visited.has (i)) continue; + // Only real links can form 32 bit spaces for (auto& l : vertices_[i].obj.real_links) { - if (l.width == 4 && !l.is_signed) + if (l.is_signed || l.width < 3) + continue; + + if (i == root_index && l.width == 3) + // Ignore 24bit links from the root node, this skips past the single 24bit + // pointer to the lookup list. + continue; + + if (l.width == 3) { - roots.add (l.objidx); - find_subgraph (l.objidx, visited); + // A 24bit offset forms a root, unless there is 32bit offsets somewhere + // in it's subgraph, then those become the roots instead. This is to make sure + // that extension subtables beneath a 24bit lookup become the spaces instead + // of the offset to the lookup. + hb_set_t sub_roots; + find_32bit_roots (l.objidx, sub_roots); + if (sub_roots) { + for (unsigned sub_root_idx : sub_roots) { + roots.add (sub_root_idx); + find_subgraph (sub_root_idx, visited); + } + continue; + } } + + roots.add (l.objidx); + find_subgraph (l.objidx, visited); } } + } - // Mark everything not in the subgraphs of 32 bit roots as visited. - // This prevents 32 bit subgraphs from being connected via nodes not in the 32 bit subgraphs. + template + vertex_and_table_t as_table (unsigned parent, const void* offset, Ts... ds) + { + return as_table_from_index (index_for_offset (parent, offset), std::forward(ds)...); + } + + template + vertex_and_table_t as_mutable_table (unsigned parent, const void* offset, Ts... ds) + { + return as_table_from_index (mutable_index_for_offset (parent, offset), std::forward(ds)...); + } + + template + vertex_and_table_t as_table_from_index (unsigned index, Ts... ds) + { + if (index >= vertices_.length) + return vertex_and_table_t (); + + vertex_and_table_t r; + r.vertex = &vertices_[index]; + r.table = (T*) r.vertex->obj.head; + r.index = index; + if (!r.table) + return vertex_and_table_t (); + + if (!r.table->sanitize (*(r.vertex), std::forward(ds)...)) + return vertex_and_table_t (); + + return r; + } + + // Finds the object id of the object pointed to by the offset at 'offset' + // within object[node_idx]. + unsigned index_for_offset (unsigned node_idx, const void* offset) const + { + const auto& node = object (node_idx); + if (offset < node.head || offset >= node.tail) return -1; + + unsigned length = node.real_links.length; + for (unsigned i = 0; i < length; i++) + { + // Use direct access for increased performance, this is a hot method. + const auto& link = node.real_links.arrayZ[i]; + if (offset != node.head + link.position) + continue; + return link.objidx; + } + + return -1; + } + + // Finds the object id of the object pointed to by the offset at 'offset' + // within object[node_idx]. Ensures that the returned object is safe to mutate. + // That is, if the original child object is shared by parents other than node_idx + // it will be duplicated and the duplicate will be returned instead. + unsigned mutable_index_for_offset (unsigned node_idx, const void* offset) + { + unsigned child_idx = index_for_offset (node_idx, offset); + auto& child = vertices_[child_idx]; + for (unsigned p : child.parents) + { + if (p != node_idx) { + return duplicate (node_idx, child_idx); + } + } + + return child_idx; + } + + + /* + * Assign unique space numbers to each connected subgraph of 24 bit and/or 32 bit offset(s). + * Currently, this is implemented specifically tailored to the structure of a GPOS/GSUB + * (including with 24bit offsets) table. + */ + bool assign_spaces () + { + update_parents (); + + hb_set_t visited; + hb_set_t roots; + find_space_roots (visited, roots); + + // Mark everything not in the subgraphs of the roots as visited. This prevents + // subgraphs from being connected via nodes not in those subgraphs. visited.invert (); if (!roots) return false; while (roots) { - unsigned next = HB_SET_VALUE_INVALID; + uint32_t next = HB_SET_VALUE_INVALID; if (unlikely (!check_success (!roots.in_error ()))) break; if (!roots.next (&next)) break; @@ -361,6 +700,9 @@ struct graph_t } } + if (in_error ()) + return false; + if (!made_changes) return false; @@ -374,8 +716,8 @@ struct graph_t auto new_subgraph = + subgraph.keys () - | hb_map([&] (unsigned node_idx) { - const unsigned *v; + | hb_map([&] (uint32_t node_idx) { + const uint32_t *v; if (index_map.has (node_idx, &v)) return *v; return node_idx; }) @@ -385,10 +727,10 @@ struct graph_t remap_obj_indices (index_map, parents.iter (), true); // Update roots set with new indices as needed. - unsigned next = HB_SET_VALUE_INVALID; + uint32_t next = HB_SET_VALUE_INVALID; while (roots.next (&next)) { - const unsigned *v; + const uint32_t *v; if (index_map.has (next, &v)) { roots.del (next); @@ -403,7 +745,7 @@ struct graph_t { for (const auto& link : vertices_[node_idx].obj.all_links ()) { - const unsigned *v; + const uint32_t *v; if (subgraph.has (link.objidx, &v)) { subgraph.set (link.objidx, *v + 1); @@ -422,6 +764,68 @@ struct graph_t find_subgraph (link.objidx, subgraph); } + size_t find_subgraph_size (unsigned node_idx, hb_set_t& subgraph, unsigned max_depth = -1) + { + if (subgraph.has (node_idx)) return 0; + subgraph.add (node_idx); + + const auto& o = vertices_[node_idx].obj; + size_t size = o.tail - o.head; + if (max_depth == 0) + return size; + + for (const auto& link : o.all_links ()) + size += find_subgraph_size (link.objidx, subgraph, max_depth - 1); + return size; + } + + /* + * Finds the topmost children of 32bit offsets in the subgraph starting + * at node_idx. Found indices are placed into 'found'. + */ + void find_32bit_roots (unsigned node_idx, hb_set_t& found) + { + for (const auto& link : vertices_[node_idx].obj.all_links ()) + { + if (!link.is_signed && link.width == 4) { + found.add (link.objidx); + continue; + } + find_32bit_roots (link.objidx, found); + } + } + + /* + * Moves the child of old_parent_idx pointed to by old_offset to a new + * vertex at the new_offset. + */ + template + void move_child (unsigned old_parent_idx, + const O* old_offset, + unsigned new_parent_idx, + const O* new_offset) + { + distance_invalid = true; + positions_invalid = true; + + auto& old_v = vertices_[old_parent_idx]; + auto& new_v = vertices_[new_parent_idx]; + + unsigned child_id = index_for_offset (old_parent_idx, + old_offset); + + auto* new_link = new_v.obj.real_links.push (); + new_link->width = O::static_size; + new_link->objidx = child_id; + new_link->position = (const char*) new_offset - (const char*) new_v.obj.head; + + auto& child = vertices_[child_id]; + child.parents.push (new_parent_idx); + + old_v.remove_real_link (child_id, old_offset); + child.remove_parent (old_parent_idx); + } + /* * duplicates all nodes in the subgraph reachable from node_idx. Does not re-assign * links. index_map is updated with mappings from old id to new id. If a duplication has already @@ -432,7 +836,11 @@ struct graph_t if (index_map.has (node_idx)) return; - index_map.set (node_idx, duplicate (node_idx)); + unsigned clone_idx = duplicate (node_idx); + if (!check_success (clone_idx != (unsigned) -1)) + return; + + index_map.set (node_idx, clone_idx); for (const auto& l : object (node_idx).all_links ()) { duplicate_subgraph (l.objidx, index_map); } @@ -490,7 +898,20 @@ struct graph_t * parent to the clone. The copy is a shallow copy, objects * linked from child are not duplicated. */ - bool duplicate (unsigned parent_idx, unsigned child_idx) + unsigned duplicate_if_shared (unsigned parent_idx, unsigned child_idx) + { + unsigned new_idx = duplicate (parent_idx, child_idx); + if (new_idx == (unsigned) -1) return child_idx; + return new_idx; + } + + + /* + * Creates a copy of child and re-assigns the link from + * parent to the clone. The copy is a shallow copy, objects + * linked from child are not duplicated. + */ + unsigned duplicate (unsigned parent_idx, unsigned child_idx) { update_parents (); @@ -504,12 +925,12 @@ struct graph_t { // Can't duplicate this node, doing so would orphan the original one as all remaining links // to child are from parent. - DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %d => %d", + DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %u => %u", parent_idx, child_idx); - return false; + return -1; } - DEBUG_MSG (SUBSET_REPACK, nullptr, " Duplicating %d => %d", + DEBUG_MSG (SUBSET_REPACK, nullptr, " Duplicating %u => %u", parent_idx, child_idx); unsigned clone_idx = duplicate (child_idx); @@ -526,7 +947,40 @@ struct graph_t reassign_link (l, parent_idx, clone_idx); } - return true; + return clone_idx; + } + + + /* + * Adds a new node to the graph, not connected to anything. + */ + unsigned new_node (char* head, char* tail) + { + positions_invalid = true; + distance_invalid = true; + + auto* clone = vertices_.push (); + if (vertices_.in_error ()) { + return -1; + } + + clone->obj.head = head; + clone->obj.tail = tail; + clone->distance = 0; + clone->space = 0; + + unsigned clone_idx = vertices_.length - 2; + + // The last object is the root of the graph, so swap back the root to the end. + // The root's obj idx does change, however since it's root nothing else refers to it. + // all other obj idx's will be unaffected. + hb_swap (vertices_[vertices_.length - 2], *clone); + + // Since the root moved, update the parents arrays of all children on the root. + for (const auto& l : root ().obj.all_links ()) + vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ()); + + return clone_idx; } /* @@ -534,7 +988,7 @@ struct graph_t */ bool raise_childrens_priority (unsigned parent_idx) { - DEBUG_MSG (SUBSET_REPACK, nullptr, " Raising priority of all children of %d", + DEBUG_MSG (SUBSET_REPACK, nullptr, " Raising priority of all children of %u", parent_idx); // This operation doesn't change ordering until a sort is run, so no need // to invalidate positions. It does not change graph structure so no need @@ -546,6 +1000,72 @@ struct graph_t return made_change; } + bool is_fully_connected () + { + update_parents(); + + if (root().parents) + // Root cannot have parents. + return false; + + for (unsigned i = 0; i < root_idx (); i++) + { + if (!vertices_[i].parents) + return false; + } + return true; + } + +#if 0 + /* + * Saves the current graph to a packed binary format which the repacker fuzzer takes + * as a seed. + */ + void save_fuzzer_seed (hb_tag_t tag) const + { + FILE* f = fopen ("./repacker_fuzzer_seed", "w"); + fwrite ((void*) &tag, sizeof (tag), 1, f); + + uint16_t num_objects = vertices_.length; + fwrite ((void*) &num_objects, sizeof (num_objects), 1, f); + + for (const auto& v : vertices_) + { + uint16_t blob_size = v.table_size (); + fwrite ((void*) &blob_size, sizeof (blob_size), 1, f); + fwrite ((const void*) v.obj.head, blob_size, 1, f); + } + + uint16_t link_count = 0; + for (const auto& v : vertices_) + link_count += v.obj.real_links.length; + + fwrite ((void*) &link_count, sizeof (link_count), 1, f); + + typedef struct + { + uint16_t parent; + uint16_t child; + uint16_t position; + uint8_t width; + } link_t; + + for (unsigned i = 0; i < vertices_.length; i++) + { + for (const auto& l : vertices_[i].obj.real_links) + { + link_t link { + (uint16_t) i, (uint16_t) l.objidx, + (uint16_t) l.position, (uint8_t) l.width + }; + fwrite ((void*) &link, sizeof (link), 1, f); + } + } + + fclose (f); + } +#endif + void print_orphaned_nodes () { if (!DEBUG_ENABLED(SUBSET_REPACK)) return; @@ -554,6 +1074,10 @@ struct graph_t parents_invalid = true; update_parents(); + if (root().parents) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Root node has incoming edges."); + } + for (unsigned i = 0; i < root_idx (); i++) { const auto& v = vertices_[i]; @@ -622,7 +1146,7 @@ struct graph_t private: /* - * Returns the numbers of incoming edges that are 32bits wide. + * Returns the numbers of incoming edges that are 24 or 32 bits wide. */ unsigned wide_parents (unsigned node_idx, hb_set_t& parents) const { @@ -636,7 +1160,9 @@ struct graph_t // Only real links can be wide for (const auto& l : vertices_[p].obj.real_links) { - if (l.objidx == node_idx && l.width == 4 && !l.is_signed) + if (l.objidx == node_idx + && (l.width == 3 || l.width == 4) + && !l.is_signed) { count++; parents.add (p); @@ -668,6 +1194,11 @@ struct graph_t } } + for (unsigned i = 0; i < vertices_.length; i++) + // parents arrays must be accurate or downstream operations like cycle detection + // and sorting won't work correctly. + check_success (!vertices_[i].parents.in_error ()); + parents_invalid = false; } @@ -786,7 +1317,7 @@ struct graph_t { for (auto& link : vertices_[i].obj.all_links_writer ()) { - const unsigned *v; + const uint32_t *v; if (!id_map.has (link.objidx, &v)) continue; if (only_wide && !(link.width == 4 && !link.is_signed)) continue; @@ -853,6 +1384,7 @@ struct graph_t bool positions_invalid; bool successful; hb_vector_t num_roots_for_space_; + hb_vector_t buffers; }; } diff --git a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.cc b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.cc new file mode 100644 index 00000000000..b2044426d46 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.cc @@ -0,0 +1,70 @@ +/* + * Copyright © 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#include "gsubgpos-graph.hh" + +namespace graph { + +gsubgpos_graph_context_t::gsubgpos_graph_context_t (hb_tag_t table_tag_, + graph_t& graph_) + : table_tag (table_tag_), + graph (graph_), + lookup_list_index (0), + lookups () +{ + if (table_tag_ != HB_OT_TAG_GPOS + && table_tag_ != HB_OT_TAG_GSUB) + return; + + GSTAR* gstar = graph::GSTAR::graph_to_gstar (graph_); + if (gstar) { + gstar->find_lookups (graph, lookups); + lookup_list_index = gstar->get_lookup_list_index (graph_); + } +} + +unsigned gsubgpos_graph_context_t::create_node (unsigned size) +{ + char* buffer = (char*) hb_calloc (1, size); + if (!buffer) + return -1; + + add_buffer (buffer); + + return graph.new_node (buffer, buffer + size); +} + +unsigned gsubgpos_graph_context_t::num_non_ext_subtables () { + unsigned count = 0; + for (auto l : lookups.values ()) + { + if (l->is_extension (table_tag)) continue; + count += l->number_of_subtables (); + } + return count; +} + +} diff --git a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.hh b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.hh new file mode 100644 index 00000000000..9fe9662e645 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.hh @@ -0,0 +1,61 @@ +/* + * Copyright © 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#include "graph.hh" +#include "../hb-ot-layout-gsubgpos.hh" + +#ifndef GRAPH_GSUBGPOS_CONTEXT_HH +#define GRAPH_GSUBGPOS_CONTEXT_HH + +namespace graph { + +struct Lookup; + +struct gsubgpos_graph_context_t +{ + hb_tag_t table_tag; + graph_t& graph; + unsigned lookup_list_index; + hb_hashmap_t lookups; + + + HB_INTERNAL gsubgpos_graph_context_t (hb_tag_t table_tag_, + graph_t& graph_); + + HB_INTERNAL unsigned create_node (unsigned size); + + void add_buffer (char* buffer) + { + graph.add_buffer (buffer); + } + + private: + HB_INTERNAL unsigned num_non_ext_subtables (); +}; + +} + +#endif // GRAPH_GSUBGPOS_CONTEXT diff --git a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh new file mode 100644 index 00000000000..c170638409f --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh @@ -0,0 +1,414 @@ +/* + * Copyright © 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#include "graph.hh" +#include "../hb-ot-layout-gsubgpos.hh" +#include "../OT/Layout/GSUB/ExtensionSubst.hh" +#include "gsubgpos-context.hh" +#include "pairpos-graph.hh" +#include "markbasepos-graph.hh" + +#ifndef GRAPH_GSUBGPOS_GRAPH_HH +#define GRAPH_GSUBGPOS_GRAPH_HH + +namespace graph { + +struct Lookup; + +template +struct ExtensionFormat1 : public OT::ExtensionFormat1 +{ + void reset(unsigned type) + { + this->format = 1; + this->extensionLookupType = type; + this->extensionOffset = 0; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + return vertex_len >= OT::ExtensionFormat1::static_size; + } + + unsigned get_lookup_type () const + { + return this->extensionLookupType; + } + + unsigned get_subtable_index (graph_t& graph, unsigned this_index) const + { + return graph.index_for_offset (this_index, &this->extensionOffset); + } +}; + +struct Lookup : public OT::Lookup +{ + unsigned number_of_subtables () const + { + return subTable.len; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::Lookup::min_size) return false; + return vertex_len >= this->get_size (); + } + + bool is_extension (hb_tag_t table_tag) const + { + return lookupType == extension_type (table_tag); + } + + bool make_extension (gsubgpos_graph_context_t& c, + unsigned this_index) + { + unsigned type = lookupType; + unsigned ext_type = extension_type (c.table_tag); + if (!ext_type || is_extension (c.table_tag)) + { + // NOOP + return true; + } + + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Promoting lookup type %u (obj %u) to extension.", + type, + this_index); + + for (unsigned i = 0; i < subTable.len; i++) + { + unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]); + if (!make_subtable_extension (c, + this_index, + subtable_index)) + return false; + } + + lookupType = ext_type; + return true; + } + + bool split_subtables_if_needed (gsubgpos_graph_context_t& c, + unsigned this_index) + { + unsigned type = lookupType; + bool is_ext = is_extension (c.table_tag); + + if (c.table_tag != HB_OT_TAG_GPOS) + return true; + + if (!is_ext && + type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair && + type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase) + return true; + + hb_vector_t>> all_new_subtables; + for (unsigned i = 0; i < subTable.len; i++) + { + unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]); + unsigned parent_index = this_index; + if (is_ext) { + unsigned ext_subtable_index = subtable_index; + parent_index = ext_subtable_index; + ExtensionFormat1* extension = + (ExtensionFormat1*) + c.graph.object (ext_subtable_index).head; + if (!extension || !extension->sanitize (c.graph.vertices_[ext_subtable_index])) + continue; + + subtable_index = extension->get_subtable_index (c.graph, ext_subtable_index); + type = extension->get_lookup_type (); + if (type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair + && type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase) + continue; + } + + hb_vector_t new_sub_tables; + switch (type) + { + case 2: + new_sub_tables = split_subtable (c, parent_index, subtable_index); break; + case 4: + new_sub_tables = split_subtable (c, parent_index, subtable_index); break; + default: + break; + } + if (new_sub_tables.in_error ()) return false; + if (!new_sub_tables) continue; + hb_pair_t>* entry = all_new_subtables.push (); + entry->first = i; + entry->second = std::move (new_sub_tables); + } + + if (all_new_subtables) { + add_sub_tables (c, this_index, type, all_new_subtables); + } + + return true; + } + + template + hb_vector_t split_subtable (gsubgpos_graph_context_t& c, + unsigned parent_idx, + unsigned objidx) + { + T* sub_table = (T*) c.graph.object (objidx).head; + if (!sub_table || !sub_table->sanitize (c.graph.vertices_[objidx])) + return hb_vector_t (); + + return sub_table->split_subtables (c, parent_idx, objidx); + } + + void add_sub_tables (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned type, + hb_vector_t>>& subtable_ids) + { + bool is_ext = is_extension (c.table_tag); + auto& v = c.graph.vertices_[this_index]; + fix_existing_subtable_links (c, this_index, subtable_ids); + + unsigned new_subtable_count = 0; + for (const auto& p : subtable_ids) + new_subtable_count += p.second.length; + + size_t new_size = v.table_size () + + new_subtable_count * OT::Offset16::static_size; + char* buffer = (char*) hb_calloc (1, new_size); + c.add_buffer (buffer); + hb_memcpy (buffer, v.obj.head, v.table_size()); + + v.obj.head = buffer; + v.obj.tail = buffer + new_size; + + Lookup* new_lookup = (Lookup*) buffer; + + unsigned shift = 0; + new_lookup->subTable.len = subTable.len + new_subtable_count; + for (const auto& p : subtable_ids) + { + unsigned offset_index = p.first + shift + 1; + shift += p.second.length; + + for (unsigned subtable_id : p.second) + { + if (is_ext) + { + unsigned ext_id = create_extension_subtable (c, subtable_id, type); + c.graph.vertices_[subtable_id].parents.push (ext_id); + subtable_id = ext_id; + } + + auto* link = v.obj.real_links.push (); + link->width = 2; + link->objidx = subtable_id; + link->position = (char*) &new_lookup->subTable[offset_index++] - + (char*) new_lookup; + c.graph.vertices_[subtable_id].parents.push (this_index); + } + } + + // Repacker sort order depends on link order, which we've messed up so resort it. + v.obj.real_links.qsort (); + + // The head location of the lookup has changed, invalidating the lookups map entry + // in the context. Update the map. + c.lookups.set (this_index, new_lookup); + } + + void fix_existing_subtable_links (gsubgpos_graph_context_t& c, + unsigned this_index, + hb_vector_t>>& subtable_ids) + { + auto& v = c.graph.vertices_[this_index]; + Lookup* lookup = (Lookup*) v.obj.head; + + unsigned shift = 0; + for (const auto& p : subtable_ids) + { + unsigned insert_index = p.first + shift; + unsigned pos_offset = p.second.length * OT::Offset16::static_size; + unsigned insert_offset = (char*) &lookup->subTable[insert_index] - (char*) lookup; + shift += p.second.length; + + for (auto& l : v.obj.all_links_writer ()) + { + if (l.position > insert_offset) l.position += pos_offset; + } + } + } + + unsigned create_extension_subtable (gsubgpos_graph_context_t& c, + unsigned subtable_index, + unsigned type) + { + unsigned extension_size = OT::ExtensionFormat1::static_size; + + unsigned ext_index = c.create_node (extension_size); + if (ext_index == (unsigned) -1) + return -1; + + auto& ext_vertex = c.graph.vertices_[ext_index]; + ExtensionFormat1* extension = + (ExtensionFormat1*) ext_vertex.obj.head; + extension->reset (type); + + // Make extension point at the subtable. + auto* l = ext_vertex.obj.real_links.push (); + + l->width = 4; + l->objidx = subtable_index; + l->position = 4; + + return ext_index; + } + + bool make_subtable_extension (gsubgpos_graph_context_t& c, + unsigned lookup_index, + unsigned subtable_index) + { + unsigned type = lookupType; + + unsigned ext_index = create_extension_subtable(c, subtable_index, type); + if (ext_index == (unsigned) -1) + return false; + + auto& lookup_vertex = c.graph.vertices_[lookup_index]; + for (auto& l : lookup_vertex.obj.real_links.writer ()) + { + if (l.objidx == subtable_index) + // Change lookup to point at the extension. + l.objidx = ext_index; + } + + // Make extension point at the subtable. + auto& ext_vertex = c.graph.vertices_[ext_index]; + auto& subtable_vertex = c.graph.vertices_[subtable_index]; + ext_vertex.parents.push (lookup_index); + subtable_vertex.remap_parent (lookup_index, ext_index); + + return true; + } + + private: + unsigned extension_type (hb_tag_t table_tag) const + { + switch (table_tag) + { + case HB_OT_TAG_GPOS: return 9; + case HB_OT_TAG_GSUB: return 7; + default: return 0; + } + } +}; + +template +struct LookupList : public OT::LookupList +{ + bool sanitize (const graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::LookupList::min_size) return false; + return vertex_len >= OT::LookupList::item_size * this->len; + } +}; + +struct GSTAR : public OT::GSUBGPOS +{ + static GSTAR* graph_to_gstar (graph_t& graph) + { + const auto& r = graph.root (); + + GSTAR* gstar = (GSTAR*) r.obj.head; + if (!gstar || !gstar->sanitize (r)) + return nullptr; + + return gstar; + } + + const void* get_lookup_list_field_offset () const + { + switch (u.version.major) { + case 1: return u.version1.get_lookup_list_offset (); +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.get_lookup_list_offset (); +#endif + default: return 0; + } + } + + bool sanitize (const graph_t::vertex_t& vertex) + { + int64_t len = vertex.obj.tail - vertex.obj.head; + if (len < OT::GSUBGPOS::min_size) return false; + return len >= get_size (); + } + + void find_lookups (graph_t& graph, + hb_hashmap_t& lookups /* OUT */) + { + switch (u.version.major) { + case 1: find_lookups (graph, lookups); break; +#ifndef HB_NO_BEYOND_64K + case 2: find_lookups (graph, lookups); break; +#endif + } + } + + unsigned get_lookup_list_index (graph_t& graph) + { + return graph.index_for_offset (graph.root_idx (), + get_lookup_list_field_offset()); + } + + template + void find_lookups (graph_t& graph, + hb_hashmap_t& lookups /* OUT */) + { + unsigned lookup_list_idx = get_lookup_list_index (graph); + const LookupList* lookupList = + (const LookupList*) graph.object (lookup_list_idx).head; + if (!lookupList || !lookupList->sanitize (graph.vertices_[lookup_list_idx])) + return; + + for (unsigned i = 0; i < lookupList->len; i++) + { + unsigned lookup_idx = graph.index_for_offset (lookup_list_idx, &(lookupList->arrayZ[i])); + Lookup* lookup = (Lookup*) graph.object (lookup_idx).head; + if (!lookup || !lookup->sanitize (graph.vertices_[lookup_idx])) continue; + lookups.set (lookup_idx, lookup); + } + } +}; + + + + +} + +#endif /* GRAPH_GSUBGPOS_GRAPH_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh new file mode 100644 index 00000000000..84ef5f71b93 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh @@ -0,0 +1,510 @@ +/* + * Copyright © 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#ifndef GRAPH_MARKBASEPOS_GRAPH_HH +#define GRAPH_MARKBASEPOS_GRAPH_HH + +#include "split-helpers.hh" +#include "coverage-graph.hh" +#include "../OT/Layout/GPOS/MarkBasePos.hh" +#include "../OT/Layout/GPOS/PosLookupSubTable.hh" + +namespace graph { + +struct AnchorMatrix : public OT::Layout::GPOS_impl::AnchorMatrix +{ + bool sanitize (graph_t::vertex_t& vertex, unsigned class_count) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < AnchorMatrix::min_size) return false; + + return vertex_len >= AnchorMatrix::min_size + + OT::Offset16::static_size * class_count * this->rows; + } + + bool shrink (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned old_class_count, + unsigned new_class_count) + { + if (new_class_count >= old_class_count) return false; + auto& o = c.graph.vertices_[this_index].obj; + unsigned base_count = rows; + o.tail = o.head + + AnchorMatrix::min_size + + OT::Offset16::static_size * base_count * new_class_count; + + // Reposition links into the new indexing scheme. + for (auto& link : o.real_links.writer ()) + { + unsigned index = (link.position - 2) / 2; + unsigned base = index / old_class_count; + unsigned klass = index % old_class_count; + if (klass >= new_class_count) + // should have already been removed + return false; + + unsigned new_index = base * new_class_count + klass; + + link.position = (char*) &(this->matrixZ[new_index]) - (char*) this; + } + + return true; + } + + unsigned clone (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned start, + unsigned end, + unsigned class_count) + { + unsigned base_count = rows; + unsigned new_class_count = end - start; + unsigned size = AnchorMatrix::min_size + + OT::Offset16::static_size * new_class_count * rows; + unsigned prime_id = c.create_node (size); + if (prime_id == (unsigned) -1) return -1; + AnchorMatrix* prime = (AnchorMatrix*) c.graph.object (prime_id).head; + prime->rows = base_count; + + auto& o = c.graph.vertices_[this_index].obj; + int num_links = o.real_links.length; + for (int i = 0; i < num_links; i++) + { + const auto& link = o.real_links[i]; + unsigned old_index = (link.position - 2) / OT::Offset16::static_size; + unsigned klass = old_index % class_count; + if (klass < start || klass >= end) continue; + + unsigned base = old_index / class_count; + unsigned new_klass = klass - start; + unsigned new_index = base * new_class_count + new_klass; + + + unsigned child_idx = link.objidx; + c.graph.add_link (&(prime->matrixZ[new_index]), + prime_id, + child_idx); + + auto& child = c.graph.vertices_[child_idx]; + child.remove_parent (this_index); + + o.real_links.remove_unordered (i); + num_links--; + i--; + } + + return prime_id; + } +}; + +struct MarkArray : public OT::Layout::GPOS_impl::MarkArray +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + unsigned min_size = MarkArray::min_size; + if (vertex_len < min_size) return false; + + return vertex_len >= get_size (); + } + + bool shrink (gsubgpos_graph_context_t& c, + const hb_hashmap_t& mark_array_links, + unsigned this_index, + unsigned new_class_count) + { + auto& o = c.graph.vertices_[this_index].obj; + for (const auto& link : o.real_links) + c.graph.vertices_[link.objidx].remove_parent (this_index); + o.real_links.reset (); + + unsigned new_index = 0; + for (const auto& record : this->iter ()) + { + unsigned klass = record.klass; + if (klass >= new_class_count) continue; + + (*this)[new_index].klass = klass; + unsigned position = (char*) &record.markAnchor - (char*) this; + unsigned* objidx; + if (!mark_array_links.has (position, &objidx)) + { + new_index++; + continue; + } + + c.graph.add_link (&(*this)[new_index].markAnchor, this_index, *objidx); + new_index++; + } + + this->len = new_index; + o.tail = o.head + MarkArray::min_size + + OT::Layout::GPOS_impl::MarkRecord::static_size * new_index; + return true; + } + + unsigned clone (gsubgpos_graph_context_t& c, + unsigned this_index, + const hb_hashmap_t& pos_to_index, + hb_set_t& marks, + unsigned start_class) + { + unsigned size = MarkArray::min_size + + OT::Layout::GPOS_impl::MarkRecord::static_size * + marks.get_population (); + unsigned prime_id = c.create_node (size); + if (prime_id == (unsigned) -1) return -1; + MarkArray* prime = (MarkArray*) c.graph.object (prime_id).head; + prime->len = marks.get_population (); + + + unsigned i = 0; + for (hb_codepoint_t mark : marks) + { + (*prime)[i].klass = (*this)[mark].klass - start_class; + unsigned offset_pos = (char*) &((*this)[mark].markAnchor) - (char*) this; + unsigned* anchor_index; + if (pos_to_index.has (offset_pos, &anchor_index)) + c.graph.move_child (this_index, + &((*this)[mark].markAnchor), + prime_id, + &((*prime)[i].markAnchor)); + + i++; + } + + return prime_id; + } +}; + +struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + return vertex_len >= MarkBasePosFormat1::static_size; + } + + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + hb_set_t visited; + + const unsigned base_coverage_id = c.graph.index_for_offset (this_index, &baseCoverage); + const unsigned base_size = + OT::Layout::GPOS_impl::PairPosFormat1_3::min_size + + MarkArray::min_size + + AnchorMatrix::min_size + + c.graph.vertices_[base_coverage_id].table_size (); + + hb_vector_t class_to_info = get_class_info (c, this_index); + + unsigned class_count = classCount; + auto base_array = c.graph.as_table (this_index, + &baseArray, + class_count); + if (!base_array) return hb_vector_t (); + unsigned base_count = base_array.table->rows; + + unsigned partial_coverage_size = 4; + unsigned accumulated = base_size; + hb_vector_t split_points; + + for (unsigned klass = 0; klass < class_count; klass++) + { + class_info_t& info = class_to_info[klass]; + partial_coverage_size += OT::HBUINT16::static_size * info.marks.get_population (); + unsigned accumulated_delta = + OT::Layout::GPOS_impl::MarkRecord::static_size * info.marks.get_population () + + OT::Offset16::static_size * base_count; + + for (unsigned objidx : info.child_indices) + accumulated_delta += c.graph.find_subgraph_size (objidx, visited); + + accumulated += accumulated_delta; + unsigned total = accumulated + partial_coverage_size; + + if (total >= (1 << 16)) + { + split_points.push (klass); + accumulated = base_size + accumulated_delta; + partial_coverage_size = 4 + OT::HBUINT16::static_size * info.marks.get_population (); + visited.clear (); // node sharing isn't allowed between splits. + } + } + + + const unsigned mark_array_id = c.graph.index_for_offset (this_index, &markArray); + split_context_t split_context { + c, + this, + c.graph.duplicate_if_shared (parent_index, this_index), + std::move (class_to_info), + c.graph.vertices_[mark_array_id].position_to_index_map (), + }; + + return actuate_subtable_split (split_context, split_points); + } + + private: + + struct class_info_t { + hb_set_t marks; + hb_vector_t child_indices; + }; + + struct split_context_t { + gsubgpos_graph_context_t& c; + MarkBasePosFormat1* thiz; + unsigned this_index; + hb_vector_t class_to_info; + hb_hashmap_t mark_array_links; + + hb_set_t marks_for (unsigned start, unsigned end) + { + hb_set_t marks; + for (unsigned klass = start; klass < end; klass++) + { + + class_to_info[klass].marks.iter () + | hb_sink (marks) + ; + } + return marks; + } + + unsigned original_count () + { + return thiz->classCount; + } + + unsigned clone_range (unsigned start, unsigned end) + { + return thiz->clone_range (*this, this->this_index, start, end); + } + + bool shrink (unsigned count) + { + return thiz->shrink (*this, this->this_index, count); + } + }; + + hb_vector_t get_class_info (gsubgpos_graph_context_t& c, + unsigned this_index) + { + hb_vector_t class_to_info; + + unsigned class_count= classCount; + class_to_info.resize (class_count); + + auto mark_array = c.graph.as_table (this_index, &markArray); + if (!mark_array) return hb_vector_t (); + unsigned mark_count = mark_array.table->len; + for (unsigned mark = 0; mark < mark_count; mark++) + { + unsigned klass = (*mark_array.table)[mark].get_class (); + class_to_info[klass].marks.add (mark); + } + + for (const auto& link : mark_array.vertex->obj.real_links) + { + unsigned mark = (link.position - 2) / + OT::Layout::GPOS_impl::MarkRecord::static_size; + unsigned klass = (*mark_array.table)[mark].get_class (); + class_to_info[klass].child_indices.push (link.objidx); + } + + unsigned base_array_id = + c.graph.index_for_offset (this_index, &baseArray); + auto& base_array_v = c.graph.vertices_[base_array_id]; + + for (const auto& link : base_array_v.obj.real_links) + { + unsigned index = (link.position - 2) / OT::Offset16::static_size; + unsigned klass = index % class_count; + class_to_info[klass].child_indices.push (link.objidx); + } + + return class_to_info; + } + + bool shrink (split_context_t& sc, + unsigned this_index, + unsigned count) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Shrinking MarkBasePosFormat1 (%u) to [0, %u).", + this_index, + count); + + unsigned old_count = classCount; + if (count >= old_count) + return true; + + classCount = count; + + auto mark_coverage = sc.c.graph.as_mutable_table (this_index, + &markCoverage); + if (!mark_coverage) return false; + hb_set_t marks = sc.marks_for (0, count); + auto new_coverage = + + hb_enumerate (mark_coverage.table->iter ()) + | hb_filter (marks, hb_first) + | hb_map_retains_sorting (hb_second) + ; + if (!Coverage::make_coverage (sc.c, + new_coverage, + mark_coverage.index, + 4 + 2 * marks.get_population ())) + return false; + + + auto base_array = sc.c.graph.as_mutable_table (this_index, + &baseArray, + old_count); + if (!base_array || !base_array.table->shrink (sc.c, + base_array.index, + old_count, + count)) + return false; + + auto mark_array = sc.c.graph.as_mutable_table (this_index, + &markArray); + if (!mark_array || !mark_array.table->shrink (sc.c, + sc.mark_array_links, + mark_array.index, + count)) + return false; + + return true; + } + + // Create a new MarkBasePos that has all of the data for classes from [start, end). + unsigned clone_range (split_context_t& sc, + unsigned this_index, + unsigned start, unsigned end) const + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Cloning MarkBasePosFormat1 (%u) range [%u, %u).", this_index, start, end); + + graph_t& graph = sc.c.graph; + unsigned prime_size = OT::Layout::GPOS_impl::MarkBasePosFormat1_2::static_size; + + unsigned prime_id = sc.c.create_node (prime_size); + if (prime_id == (unsigned) -1) return -1; + + MarkBasePosFormat1* prime = (MarkBasePosFormat1*) graph.object (prime_id).head; + prime->format = this->format; + unsigned new_class_count = end - start; + prime->classCount = new_class_count; + + unsigned base_coverage_id = + graph.index_for_offset (sc.this_index, &baseCoverage); + graph.add_link (&(prime->baseCoverage), prime_id, base_coverage_id); + graph.duplicate (prime_id, base_coverage_id); + + auto mark_coverage = sc.c.graph.as_table (this_index, + &markCoverage); + if (!mark_coverage) return false; + hb_set_t marks = sc.marks_for (start, end); + auto new_coverage = + + hb_enumerate (mark_coverage.table->iter ()) + | hb_filter (marks, hb_first) + | hb_map_retains_sorting (hb_second) + ; + if (!Coverage::add_coverage (sc.c, + prime_id, + 2, + + new_coverage, + marks.get_population () * 2 + 4)) + return -1; + + auto mark_array = + graph.as_table (sc.this_index, &markArray); + if (!mark_array) return -1; + unsigned new_mark_array = + mark_array.table->clone (sc.c, + mark_array.index, + sc.mark_array_links, + marks, + start); + graph.add_link (&(prime->markArray), prime_id, new_mark_array); + + unsigned class_count = classCount; + auto base_array = + graph.as_table (sc.this_index, &baseArray, class_count); + if (!base_array) return -1; + unsigned new_base_array = + base_array.table->clone (sc.c, + base_array.index, + start, end, this->classCount); + graph.add_link (&(prime->baseArray), prime_id, new_base_array); + + return prime_id; + } +}; + + +struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos +{ + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + switch (u.format) { + case 1: + return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); +#ifndef HB_NO_BEYOND_64K + case 2: HB_FALLTHROUGH; + // Don't split 24bit PairPos's. +#endif + default: + return hb_vector_t (); + } + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < u.format.get_size ()) return false; + + switch (u.format) { + case 1: + return ((MarkBasePosFormat1*)(&u.format1))->sanitize (vertex); +#ifndef HB_NO_BEYOND_64K + case 2: HB_FALLTHROUGH; +#endif + default: + // We don't handle format 3 and 4 here. + return false; + } + } +}; + + +} + +#endif // GRAPH_MARKBASEPOS_GRAPH_HH diff --git a/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh new file mode 100644 index 00000000000..1c13eb24f94 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh @@ -0,0 +1,647 @@ +/* + * Copyright © 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#ifndef GRAPH_PAIRPOS_GRAPH_HH +#define GRAPH_PAIRPOS_GRAPH_HH + +#include "split-helpers.hh" +#include "coverage-graph.hh" +#include "classdef-graph.hh" +#include "../OT/Layout/GPOS/PairPos.hh" +#include "../OT/Layout/GPOS/PosLookupSubTable.hh" + +namespace graph { + +struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size; + if (vertex_len < min_size) return false; + + return vertex_len >= + min_size + pairSet.get_size () - pairSet.len.get_size(); + } + + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + hb_set_t visited; + + const unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); + const unsigned coverage_size = c.graph.vertices_[coverage_id].table_size (); + const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size; + + unsigned partial_coverage_size = 4; + unsigned accumulated = base_size; + hb_vector_t split_points; + for (unsigned i = 0; i < pairSet.len; i++) + { + unsigned pair_set_index = pair_set_graph_index (c, this_index, i); + unsigned accumulated_delta = + c.graph.find_subgraph_size (pair_set_index, visited) + + SmallTypes::size; // for PairSet offset. + partial_coverage_size += OT::HBUINT16::static_size; + + accumulated += accumulated_delta; + unsigned total = accumulated + hb_min (partial_coverage_size, coverage_size); + + if (total >= (1 << 16)) + { + split_points.push (i); + accumulated = base_size + accumulated_delta; + partial_coverage_size = 6; + visited.clear (); // node sharing isn't allowed between splits. + } + } + + split_context_t split_context { + c, + this, + c.graph.duplicate_if_shared (parent_index, this_index), + }; + + return actuate_subtable_split (split_context, split_points); + } + + private: + + struct split_context_t { + gsubgpos_graph_context_t& c; + PairPosFormat1* thiz; + unsigned this_index; + + unsigned original_count () + { + return thiz->pairSet.len; + } + + unsigned clone_range (unsigned start, unsigned end) + { + return thiz->clone_range (this->c, this->this_index, start, end); + } + + bool shrink (unsigned count) + { + return thiz->shrink (this->c, this->this_index, count); + } + }; + + bool shrink (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned count) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Shrinking PairPosFormat1 (%u) to [0, %u).", + this_index, + count); + unsigned old_count = pairSet.len; + if (count >= old_count) + return true; + + pairSet.len = count; + c.graph.vertices_[this_index].obj.tail -= (old_count - count) * SmallTypes::size; + + auto coverage = c.graph.as_mutable_table (this_index, &this->coverage); + if (!coverage) return false; + + unsigned coverage_size = coverage.vertex->table_size (); + auto new_coverage = + + hb_zip (coverage.table->iter (), hb_range ()) + | hb_filter ([&] (hb_pair_t p) { + return p.second < count; + }) + | hb_map_retains_sorting (hb_first) + ; + + return Coverage::make_coverage (c, new_coverage, coverage.index, coverage_size); + } + + // Create a new PairPos including PairSet's from start (inclusive) to end (exclusive). + // Returns object id of the new object. + unsigned clone_range (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned start, unsigned end) const + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Cloning PairPosFormat1 (%u) range [%u, %u).", this_index, start, end); + + unsigned num_pair_sets = end - start; + unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size + + num_pair_sets * SmallTypes::size; + + unsigned pair_pos_prime_id = c.create_node (prime_size); + if (pair_pos_prime_id == (unsigned) -1) return -1; + + PairPosFormat1* pair_pos_prime = (PairPosFormat1*) c.graph.object (pair_pos_prime_id).head; + pair_pos_prime->format = this->format; + pair_pos_prime->valueFormat[0] = this->valueFormat[0]; + pair_pos_prime->valueFormat[1] = this->valueFormat[1]; + pair_pos_prime->pairSet.len = num_pair_sets; + + for (unsigned i = start; i < end; i++) + { + c.graph.move_child<> (this_index, + &pairSet[i], + pair_pos_prime_id, + &pair_pos_prime->pairSet[i - start]); + } + + unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); + if (!Coverage::clone_coverage (c, + coverage_id, + pair_pos_prime_id, + 2, + start, end)) + return -1; + + return pair_pos_prime_id; + } + + + + unsigned pair_set_graph_index (gsubgpos_graph_context_t& c, unsigned this_index, unsigned i) const + { + return c.graph.index_for_offset (this_index, &pairSet[i]); + } +}; + +struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + size_t vertex_len = vertex.table_size (); + unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size; + if (vertex_len < min_size) return false; + + const unsigned class1_count = class1Count; + return vertex_len >= + min_size + class1_count * get_class1_record_size (); + } + + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size; + const unsigned class_def_2_size = size_of (c, this_index, &classDef2); + const Coverage* coverage = get_coverage (c, this_index); + const ClassDef* class_def_1 = get_class_def_1 (c, this_index); + auto gid_and_class = + + coverage->iter () + | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { + return hb_pair_t (gid, class_def_1->get_class (gid)); + }) + ; + class_def_size_estimator_t estimator (gid_and_class); + + const unsigned class1_count = class1Count; + const unsigned class2_count = class2Count; + const unsigned class1_record_size = get_class1_record_size (); + + const unsigned value_1_len = valueFormat1.get_len (); + const unsigned value_2_len = valueFormat2.get_len (); + const unsigned total_value_len = value_1_len + value_2_len; + + unsigned accumulated = base_size; + unsigned coverage_size = 4; + unsigned class_def_1_size = 4; + unsigned max_coverage_size = coverage_size; + unsigned max_class_def_1_size = class_def_1_size; + + hb_vector_t split_points; + + hb_hashmap_t device_tables = get_all_device_tables (c, this_index); + hb_vector_t format1_device_table_indices = valueFormat1.get_device_table_indices (); + hb_vector_t format2_device_table_indices = valueFormat2.get_device_table_indices (); + bool has_device_tables = bool(format1_device_table_indices) || bool(format2_device_table_indices); + + hb_set_t visited; + for (unsigned i = 0; i < class1_count; i++) + { + unsigned accumulated_delta = class1_record_size; + coverage_size += estimator.incremental_coverage_size (i); + class_def_1_size += estimator.incremental_class_def_size (i); + max_coverage_size = hb_max (max_coverage_size, coverage_size); + max_class_def_1_size = hb_max (max_class_def_1_size, class_def_1_size); + + if (has_device_tables) { + for (unsigned j = 0; j < class2_count; j++) + { + unsigned value1_index = total_value_len * (class2_count * i + j); + unsigned value2_index = value1_index + value_1_len; + accumulated_delta += size_of_value_record_children (c, + device_tables, + format1_device_table_indices, + value1_index, + visited); + accumulated_delta += size_of_value_record_children (c, + device_tables, + format2_device_table_indices, + value2_index, + visited); + } + } + + accumulated += accumulated_delta; + unsigned total = accumulated + + coverage_size + class_def_1_size + class_def_2_size + // The largest object will pack last and can exceed the size limit. + - hb_max (hb_max (coverage_size, class_def_1_size), class_def_2_size); + if (total >= (1 << 16)) + { + split_points.push (i); + // split does not include i, so add the size for i when we reset the size counters. + accumulated = base_size + accumulated_delta; + coverage_size = 4 + estimator.incremental_coverage_size (i); + class_def_1_size = 4 + estimator.incremental_class_def_size (i); + visited.clear (); // node sharing isn't allowed between splits. + } + } + + split_context_t split_context { + c, + this, + c.graph.duplicate_if_shared (parent_index, this_index), + class1_record_size, + total_value_len, + value_1_len, + value_2_len, + max_coverage_size, + max_class_def_1_size, + device_tables, + format1_device_table_indices, + format2_device_table_indices + }; + + return actuate_subtable_split (split_context, split_points); + } + private: + + struct split_context_t + { + gsubgpos_graph_context_t& c; + PairPosFormat2* thiz; + unsigned this_index; + unsigned class1_record_size; + unsigned value_record_len; + unsigned value1_record_len; + unsigned value2_record_len; + unsigned max_coverage_size; + unsigned max_class_def_size; + + const hb_hashmap_t& device_tables; + const hb_vector_t& format1_device_table_indices; + const hb_vector_t& format2_device_table_indices; + + unsigned original_count () + { + return thiz->class1Count; + } + + unsigned clone_range (unsigned start, unsigned end) + { + return thiz->clone_range (*this, start, end); + } + + bool shrink (unsigned count) + { + return thiz->shrink (*this, count); + } + }; + + size_t get_class1_record_size () const + { + const size_t class2_count = class2Count; + return + class2_count * (valueFormat1.get_size () + valueFormat2.get_size ()); + } + + unsigned clone_range (split_context_t& split_context, + unsigned start, unsigned end) const + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Cloning PairPosFormat2 (%u) range [%u, %u).", split_context.this_index, start, end); + + graph_t& graph = split_context.c.graph; + + unsigned num_records = end - start; + unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size + + num_records * split_context.class1_record_size; + + unsigned pair_pos_prime_id = split_context.c.create_node (prime_size); + if (pair_pos_prime_id == (unsigned) -1) return -1; + + PairPosFormat2* pair_pos_prime = + (PairPosFormat2*) graph.object (pair_pos_prime_id).head; + pair_pos_prime->format = this->format; + pair_pos_prime->valueFormat1 = this->valueFormat1; + pair_pos_prime->valueFormat2 = this->valueFormat2; + pair_pos_prime->class1Count = num_records; + pair_pos_prime->class2Count = this->class2Count; + clone_class1_records (split_context, + pair_pos_prime_id, + start, + end); + + unsigned coverage_id = + graph.index_for_offset (split_context.this_index, &coverage); + unsigned class_def_1_id = + graph.index_for_offset (split_context.this_index, &classDef1); + auto& coverage_v = graph.vertices_[coverage_id]; + auto& class_def_1_v = graph.vertices_[class_def_1_id]; + Coverage* coverage_table = (Coverage*) coverage_v.obj.head; + ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head; + if (!coverage_table + || !coverage_table->sanitize (coverage_v) + || !class_def_1_table + || !class_def_1_table->sanitize (class_def_1_v)) + return -1; + + auto klass_map = + + coverage_table->iter () + | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { + return hb_pair_t (gid, class_def_1_table->get_class (gid)); + }) + | hb_filter ([&] (hb_codepoint_t klass) { + return klass >= start && klass < end; + }, hb_second) + | hb_map_retains_sorting ([&] (hb_pair_t gid_and_class) { + // Classes must be from 0...N so subtract start + return hb_pair_t (gid_and_class.first, gid_and_class.second - start); + }) + ; + + if (!Coverage::add_coverage (split_context.c, + pair_pos_prime_id, + 2, + + klass_map | hb_map_retains_sorting (hb_first), + split_context.max_coverage_size)) + return -1; + + // classDef1 + if (!ClassDef::add_class_def (split_context.c, + pair_pos_prime_id, + 8, + + klass_map, + split_context.max_class_def_size)) + return -1; + + // classDef2 + unsigned class_def_2_id = + graph.index_for_offset (split_context.this_index, &classDef2); + auto* class_def_link = graph.vertices_[pair_pos_prime_id].obj.real_links.push (); + class_def_link->width = SmallTypes::size; + class_def_link->objidx = class_def_2_id; + class_def_link->position = 10; + graph.vertices_[class_def_2_id].parents.push (pair_pos_prime_id); + graph.duplicate (pair_pos_prime_id, class_def_2_id); + + return pair_pos_prime_id; + } + + void clone_class1_records (split_context_t& split_context, + unsigned pair_pos_prime_id, + unsigned start, unsigned end) const + { + PairPosFormat2* pair_pos_prime = + (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head; + + char* start_addr = ((char*)&values[0]) + start * split_context.class1_record_size; + unsigned num_records = end - start; + hb_memcpy (&pair_pos_prime->values[0], + start_addr, + num_records * split_context.class1_record_size); + + if (!split_context.format1_device_table_indices + && !split_context.format2_device_table_indices) + // No device tables to move over. + return; + + unsigned class2_count = class2Count; + for (unsigned i = start; i < end; i++) + { + for (unsigned j = 0; j < class2_count; j++) + { + unsigned value1_index = split_context.value_record_len * (class2_count * i + j); + unsigned value2_index = value1_index + split_context.value1_record_len; + + unsigned new_value1_index = split_context.value_record_len * (class2_count * (i - start) + j); + unsigned new_value2_index = new_value1_index + split_context.value1_record_len; + + transfer_device_tables (split_context, + pair_pos_prime_id, + split_context.format1_device_table_indices, + value1_index, + new_value1_index); + + transfer_device_tables (split_context, + pair_pos_prime_id, + split_context.format2_device_table_indices, + value2_index, + new_value2_index); + } + } + } + + void transfer_device_tables (split_context_t& split_context, + unsigned pair_pos_prime_id, + const hb_vector_t& device_table_indices, + unsigned old_value_record_index, + unsigned new_value_record_index) const + { + PairPosFormat2* pair_pos_prime = + (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head; + + for (unsigned i : device_table_indices) + { + OT::Offset16* record = (OT::Offset16*) &values[old_value_record_index + i]; + unsigned record_position = ((char*) record) - ((char*) this); + if (!split_context.device_tables.has (record_position)) continue; + + split_context.c.graph.move_child ( + split_context.this_index, + record, + pair_pos_prime_id, + (OT::Offset16*) &pair_pos_prime->values[new_value_record_index + i]); + } + } + + bool shrink (split_context_t& split_context, + unsigned count) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Shrinking PairPosFormat2 (%u) to [0, %u).", + split_context.this_index, + count); + unsigned old_count = class1Count; + if (count >= old_count) + return true; + + graph_t& graph = split_context.c.graph; + class1Count = count; + graph.vertices_[split_context.this_index].obj.tail -= + (old_count - count) * split_context.class1_record_size; + + auto coverage = + graph.as_mutable_table (split_context.this_index, &this->coverage); + if (!coverage) return false; + + auto class_def_1 = + graph.as_mutable_table (split_context.this_index, &classDef1); + if (!class_def_1) return false; + + auto klass_map = + + coverage.table->iter () + | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { + return hb_pair_t (gid, class_def_1.table->get_class (gid)); + }) + | hb_filter ([&] (hb_codepoint_t klass) { + return klass < count; + }, hb_second) + ; + + auto new_coverage = + klass_map | hb_map_retains_sorting (hb_first); + if (!Coverage::make_coverage (split_context.c, + + new_coverage, + coverage.index, + // existing ranges my not be kept, worst case size is a format 1 + // coverage table. + 4 + new_coverage.len() * 2)) + return false; + + return ClassDef::make_class_def (split_context.c, + + klass_map, + class_def_1.index, + class_def_1.vertex->table_size ()); + } + + hb_hashmap_t + get_all_device_tables (gsubgpos_graph_context_t& c, + unsigned this_index) const + { + const auto& v = c.graph.vertices_[this_index]; + return v.position_to_index_map (); + } + + const Coverage* get_coverage (gsubgpos_graph_context_t& c, + unsigned this_index) const + { + unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); + auto& coverage_v = c.graph.vertices_[coverage_id]; + + Coverage* coverage_table = (Coverage*) coverage_v.obj.head; + if (!coverage_table || !coverage_table->sanitize (coverage_v)) + return &Null(Coverage); + return coverage_table; + } + + const ClassDef* get_class_def_1 (gsubgpos_graph_context_t& c, + unsigned this_index) const + { + unsigned class_def_1_id = c.graph.index_for_offset (this_index, &classDef1); + auto& class_def_1_v = c.graph.vertices_[class_def_1_id]; + + ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head; + if (!class_def_1_table || !class_def_1_table->sanitize (class_def_1_v)) + return &Null(ClassDef); + return class_def_1_table; + } + + unsigned size_of_value_record_children (gsubgpos_graph_context_t& c, + const hb_hashmap_t& device_tables, + const hb_vector_t device_table_indices, + unsigned value_record_index, + hb_set_t& visited) + { + unsigned size = 0; + for (unsigned i : device_table_indices) + { + OT::Layout::GPOS_impl::Value* record = &values[value_record_index + i]; + unsigned record_position = ((char*) record) - ((char*) this); + unsigned* obj_idx; + if (!device_tables.has (record_position, &obj_idx)) continue; + size += c.graph.find_subgraph_size (*obj_idx, visited); + } + return size; + } + + unsigned size_of (gsubgpos_graph_context_t& c, + unsigned this_index, + const void* offset) const + { + const unsigned id = c.graph.index_for_offset (this_index, offset); + return c.graph.vertices_[id].table_size (); + } +}; + +struct PairPos : public OT::Layout::GPOS_impl::PairPos +{ + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + switch (u.format) { + case 1: + return ((PairPosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); + case 2: + return ((PairPosFormat2*)(&u.format2))->split_subtables (c, parent_index, this_index); +#ifndef HB_NO_BEYOND_64K + case 3: HB_FALLTHROUGH; + case 4: HB_FALLTHROUGH; + // Don't split 24bit PairPos's. +#endif + default: + return hb_vector_t (); + } + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < u.format.get_size ()) return false; + + switch (u.format) { + case 1: + return ((PairPosFormat1*)(&u.format1))->sanitize (vertex); + case 2: + return ((PairPosFormat2*)(&u.format2))->sanitize (vertex); +#ifndef HB_NO_BEYOND_64K + case 3: HB_FALLTHROUGH; + case 4: HB_FALLTHROUGH; +#endif + default: + // We don't handle format 3 and 4 here. + return false; + } + } +}; + +} + +#endif // GRAPH_PAIRPOS_GRAPH_HH diff --git a/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh b/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh index ecc6cc5aea2..040fd1de5fd 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh @@ -33,6 +33,23 @@ struct overflow_record_t { unsigned parent; unsigned child; + + bool operator != (const overflow_record_t o) const + { return !(*this == o); } + + inline bool operator == (const overflow_record_t& o) const + { + return parent == o.parent && + child == o.child; + } + + inline uint32_t hash () const + { + uint32_t current = 0; + current = current * 31 + hb_hash (parent); + current = current * 31 + hb_hash (child); + return current; + } }; inline @@ -94,6 +111,7 @@ will_overflow (graph_t& graph, if (overflows) overflows->resize (0); graph.update_positions (); + hb_hashmap_t record_set; const auto& vertices = graph.vertices_; for (int parent_idx = vertices.length - 1; parent_idx >= 0; parent_idx--) { @@ -109,7 +127,10 @@ will_overflow (graph_t& graph, overflow_record_t r; r.parent = parent_idx; r.child = link.objidx; + if (record_set.has(&r)) continue; // don't keep duplicate overflows. + overflows->push (r); + record_set.set(&r, true); } } @@ -132,8 +153,8 @@ void print_overflows (graph_t& graph, const auto& child = graph.vertices_[o.child]; DEBUG_MSG (SUBSET_REPACK, nullptr, " overflow from " - "%4d (%4d in, %4d out, space %2d) => " - "%4d (%4d in, %4d out, space %2d)", + "%4u (%4u in, %4u out, space %2u) => " + "%4u (%4u in, %4u out, space %2u)", o.parent, parent.incoming_edges (), parent.obj.real_links.length + parent.obj.virtual_links.length, @@ -144,7 +165,7 @@ void print_overflows (graph_t& graph, graph.space_for (o.child)); } if (overflows.length > 10) { - DEBUG_MSG (SUBSET_REPACK, nullptr, " ... plus %d more overflows.", overflows.length - 10); + DEBUG_MSG (SUBSET_REPACK, nullptr, " ... plus %u more overflows.", overflows.length - 10); } } @@ -223,7 +244,7 @@ inline hb_blob_t* serialize (const graph_t& graph) return nullptr; } - memcpy (start, vertices[i].obj.head, size); + hb_memcpy (start, vertices[i].obj.head, size); // Only real links needs to be serialized. for (const auto& link : vertices[i].obj.real_links) diff --git a/src/java.desktop/share/native/libharfbuzz/graph/split-helpers.hh b/src/java.desktop/share/native/libharfbuzz/graph/split-helpers.hh new file mode 100644 index 00000000000..61fd7c2d2ff --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/graph/split-helpers.hh @@ -0,0 +1,69 @@ +/* + * Copyright © 2022 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#ifndef GRAPH_SPLIT_HELPERS_HH +#define GRAPH_SPLIT_HELPERS_HH + +namespace graph { + +template +HB_INTERNAL +hb_vector_t actuate_subtable_split (Context& split_context, + const hb_vector_t& split_points) +{ + hb_vector_t new_objects; + if (!split_points) + return new_objects; + + for (unsigned i = 0; i < split_points.length; i++) + { + unsigned start = split_points[i]; + unsigned end = (i < split_points.length - 1) + ? split_points[i + 1] + : split_context.original_count (); + unsigned id = split_context.clone_range (start, end); + + if (id == (unsigned) -1) + { + new_objects.reset (); + new_objects.allocated = -1; // mark error + return new_objects; + } + new_objects.push (id); + } + + if (!split_context.shrink (split_points[0])) + { + new_objects.reset (); + new_objects.allocated = -1; // mark error + } + + return new_objects; +} + +} + +#endif // GRAPH_SPLIT_HELPERS_HH diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh index ed376b9efc7..24d53e224c7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh @@ -42,7 +42,7 @@ struct BaselineTableFormat0Part bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); + return_trace (c->check_struct (this)); } protected: @@ -78,7 +78,7 @@ struct BaselineTableFormat2Part bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); + return_trace (c->check_struct (this)); } protected: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh index b8c9b992fb8..3d633845a0f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh @@ -28,6 +28,7 @@ #define HB_AAT_LAYOUT_COMMON_HH #include "hb-aat-layout.hh" +#include "hb-aat-map.hh" #include "hb-open-type.hh" namespace OT { @@ -39,6 +40,43 @@ namespace AAT { using namespace OT; +struct ankr; + +struct hb_aat_apply_context_t : + hb_dispatch_context_t +{ + const char *get_name () { return "APPLY"; } + template + return_t dispatch (const T &obj) { return obj.apply (this); } + static return_t default_return_value () { return false; } + bool stop_sublookup_iteration (return_t r) const { return r; } + + const hb_ot_shape_plan_t *plan; + hb_font_t *font; + hb_face_t *face; + hb_buffer_t *buffer; + hb_sanitize_context_t sanitizer; + const ankr *ankr_table; + const OT::GDEF *gdef_table; + const hb_sorted_vector_t *range_flags = nullptr; + hb_mask_t subtable_flags = 0; + + /* Unused. For debug tracing only. */ + unsigned int lookup_index; + + HB_INTERNAL hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_, + hb_font_t *font_, + hb_buffer_t *buffer_, + hb_blob_t *blob = const_cast (&Null (hb_blob_t))); + + HB_INTERNAL ~hb_aat_apply_context_t (); + + HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_); + + void set_lookup_index (unsigned int i) { lookup_index = i; } +}; + + /* * Lookup Table */ @@ -415,18 +453,7 @@ struct Lookup public: DEFINE_SIZE_UNION (2, format); }; -/* Lookup 0 has unbounded size (dependant on num_glyphs). So we need to defined - * special NULL objects for Lookup<> objects, but since it's template our macros - * don't work. So we have to hand-code them here. UGLY. */ -} /* Close namespace. */ -/* Ugly hand-coded null objects for template Lookup<> :(. */ -extern HB_INTERNAL const unsigned char _hb_Null_AAT_Lookup[2]; -template -struct Null> { - static AAT::Lookup const & get_null () - { return *reinterpret_cast *> (_hb_Null_AAT_Lookup); } -}; -namespace AAT { +DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2); enum { DELETED_GLYPH = 0xFFFF }; @@ -681,6 +708,13 @@ struct ObsoleteTypes const void *base, const T *array) { + /* https://github.com/harfbuzz/harfbuzz/issues/3483 */ + /* If offset is less than base, return an offset that would + * result in an address half a 32bit address-space away, + * to make sure sanitize fails even on 32bit builds. */ + if (unlikely (offset < unsigned ((const char *) array - (const char *) base))) + return INT_MAX / T::static_size; + /* https://github.com/harfbuzz/harfbuzz/issues/2816 */ return (offset - unsigned ((const char *) array - (const char *) base)) / T::static_size; } @@ -744,16 +778,44 @@ struct StateTableDriver num_glyphs (face_->get_num_glyphs ()) {} template - void drive (context_t *c) + void drive (context_t *c, hb_aat_apply_context_t *ac) { if (!c->in_place) buffer->clear_output (); int state = StateTableT::STATE_START_OF_TEXT; + // If there's only one range, we already checked the flag. + auto *last_range = ac->range_flags && (ac->range_flags->length > 1) ? &(*ac->range_flags)[0] : nullptr; for (buffer->idx = 0; buffer->successful;) { + /* This block is copied in NoncontextualSubtable::apply. Keep in sync. */ + if (last_range) + { + auto *range = last_range; + if (buffer->idx < buffer->len) + { + unsigned cluster = buffer->cur().cluster; + while (cluster < range->cluster_first) + range--; + while (cluster > range->cluster_last) + range++; + + + last_range = range; + } + if (!(range->flags & ac->subtable_flags)) + { + if (buffer->idx == buffer->len || unlikely (!buffer->successful)) + break; + + state = StateTableT::STATE_START_OF_TEXT; + (void) buffer->next_glyph (); + continue; + } + } + unsigned int klass = buffer->idx < buffer->len ? - machine.get_class (buffer->info[buffer->idx].codepoint, num_glyphs) : + machine.get_class (buffer->cur().codepoint, num_glyphs) : (unsigned) StateTableT::CLASS_END_OF_TEXT; DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx); const EntryT &entry = machine.get_entry (state, klass); @@ -849,41 +911,6 @@ struct StateTableDriver }; -struct ankr; - -struct hb_aat_apply_context_t : - hb_dispatch_context_t -{ - const char *get_name () { return "APPLY"; } - template - return_t dispatch (const T &obj) { return obj.apply (this); } - static return_t default_return_value () { return false; } - bool stop_sublookup_iteration (return_t r) const { return r; } - - const hb_ot_shape_plan_t *plan; - hb_font_t *font; - hb_face_t *face; - hb_buffer_t *buffer; - hb_sanitize_context_t sanitizer; - const ankr *ankr_table; - const OT::GDEF *gdef_table; - - /* Unused. For debug tracing only. */ - unsigned int lookup_index; - - HB_INTERNAL hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_, - hb_font_t *font_, - hb_buffer_t *buffer_, - hb_blob_t *blob = const_cast (&Null (hb_blob_t))); - - HB_INTERNAL ~hb_aat_apply_context_t (); - - HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_); - - void set_lookup_index (unsigned int i) { lookup_index = i; } -}; - - } /* namespace AAT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh index 527bfe45b35..04260a94616 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh @@ -62,7 +62,7 @@ struct SettingName bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); + return_trace (c->check_struct (this)); } protected: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh index 4b1319ac31c..0588310b53b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh @@ -48,7 +48,7 @@ struct ActionSubrecordHeader bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); + return_trace (c->check_struct (this)); } HBUINT16 actionClass; /* The JustClass value associated with this @@ -65,14 +65,14 @@ struct DecompositionAction bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); + return_trace (c->check_struct (this)); } ActionSubrecordHeader header; - HBFixed lowerLimit; /* If the distance factor is less than this value, + F16DOT16 lowerLimit; /* If the distance factor is less than this value, * then the ligature is decomposed. */ - HBFixed upperLimit; /* If the distance factor is greater than this value, + F16DOT16 upperLimit; /* If the distance factor is greater than this value, * then the ligature is decomposed. */ HBUINT16 order; /* Numerical order in which this ligature will * be decomposed; you may want infrequent ligatures @@ -112,13 +112,13 @@ struct ConditionalAddGlyphAction bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); + return_trace (c->check_struct (this)); } protected: ActionSubrecordHeader header; - HBFixed substThreshold; /* Distance growth factor (in ems) at which + F16DOT16 substThreshold; /* Distance growth factor (in ems) at which * this glyph is replaced and the growth factor * recalculated. */ HBGlyphID16 addGlyph; /* Glyph to be added as kashida. If this value is @@ -137,7 +137,7 @@ struct DuctileGlyphAction bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); + return_trace (c->check_struct (this)); } protected: @@ -146,13 +146,13 @@ struct DuctileGlyphAction HBUINT32 variationAxis; /* The 4-byte tag identifying the ductile axis. * This would normally be 0x64756374 ('duct'), * but you may use any axis the font contains. */ - HBFixed minimumLimit; /* The lowest value for the ductility axis that + F16DOT16 minimumLimit; /* The lowest value for the ductility axis that * still yields an acceptable appearance. Normally * this will be 1.0. */ - HBFixed noStretchValue; /* This is the default value that corresponds to + F16DOT16 noStretchValue; /* This is the default value that corresponds to * no change in appearance. Normally, this will * be 1.0. */ - HBFixed maximumLimit; /* The highest value for the ductility axis that + F16DOT16 maximumLimit; /* The highest value for the ductility axis that * still yields an acceptable appearance. */ public: DEFINE_SIZE_STATIC (22); @@ -163,7 +163,7 @@ struct RepeatedAddGlyphAction bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); + return_trace (c->check_struct (this)); } protected: @@ -271,14 +271,14 @@ struct JustWidthDeltaEntry }; protected: - HBFixed beforeGrowLimit;/* The ratio by which the advance width of the + F16DOT16 beforeGrowLimit;/* The ratio by which the advance width of the * glyph is permitted to grow on the left or top side. */ - HBFixed beforeShrinkLimit; + F16DOT16 beforeShrinkLimit; /* The ratio by which the advance width of the * glyph is permitted to shrink on the left or top side. */ - HBFixed afterGrowLimit; /* The ratio by which the advance width of the glyph + F16DOT16 afterGrowLimit; /* The ratio by which the advance width of the glyph * is permitted to shrink on the left or top side. */ - HBFixed afterShrinkLimit; + F16DOT16 afterShrinkLimit; /* The ratio by which the advance width of the glyph * is at most permitted to shrink on the right or * bottom side. */ @@ -294,7 +294,7 @@ struct WidthDeltaPair bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); + return_trace (c->check_struct (this)); } protected: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh index 51cb106bb08..2bc7b03ce89 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh @@ -350,7 +350,7 @@ struct KerxSubTableFormat1 driver_context_t dc (this, c); StateTableDriver driver (machine, c->buffer, c->font->face); - driver.drive (&dc); + driver.drive (&dc, c); return_trace (true); } @@ -594,7 +594,7 @@ struct KerxSubTableFormat4 driver_context_t dc (this, c); StateTableDriver driver (machine, c->buffer, c->font->face); - driver.drive (&dc); + driver.drive (&dc, c); return_trace (true); } @@ -751,7 +751,7 @@ struct KerxSubTableHeader bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); + return_trace (c->check_struct (this)); } public: @@ -869,6 +869,8 @@ struct KerxTable bool apply (AAT::hb_aat_apply_context_t *c) const { + c->buffer->unsafe_to_concat (); + typedef typename T::SubTable SubTable; bool ret = false; @@ -889,7 +891,7 @@ struct KerxTable reverse = bool (st->u.header.coverage & st->u.header.Backwards) != HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); - if (!c->buffer->message (c->font, "start subtable %d", c->lookup_index)) + if (!c->buffer->message (c->font, "start subtable %u", c->lookup_index)) goto skip; if (!seenCrossStream && @@ -921,7 +923,7 @@ struct KerxTable if (reverse) c->buffer->reverse (); - (void) c->buffer->message (c->font, "end subtable %d", c->lookup_index); + (void) c->buffer->message (c->font, "end subtable %u", c->lookup_index); skip: st = &StructAfter (*st); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh index 889784e7d52..81e126d5eb1 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh @@ -131,14 +131,14 @@ struct RearrangementSubtable hb_glyph_info_t *info = buffer->info; hb_glyph_info_t buf[4]; - memcpy (buf, info + start, l * sizeof (buf[0])); - memcpy (buf + 2, info + end - r, r * sizeof (buf[0])); + hb_memcpy (buf, info + start, l * sizeof (buf[0])); + hb_memcpy (buf + 2, info + end - r, r * sizeof (buf[0])); if (l != r) memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0])); - memcpy (info + start, buf + 2, r * sizeof (buf[0])); - memcpy (info + end - l, buf, l * sizeof (buf[0])); + hb_memcpy (info + start, buf + 2, r * sizeof (buf[0])); + hb_memcpy (info + end - l, buf, l * sizeof (buf[0])); if (reverse_l) { buf[0] = info[end - 1]; @@ -169,7 +169,7 @@ struct RearrangementSubtable driver_context_t dc (this); StateTableDriver driver (machine, c->buffer, c->face); - driver.drive (&dc); + driver.drive (&dc, c); return_trace (dc.ret); } @@ -325,7 +325,7 @@ struct ContextualSubtable driver_context_t dc (this, c); StateTableDriver driver (machine, c->buffer, c->face); - driver.drive (&dc); + driver.drive (&dc, c); return_trace (dc.ret); } @@ -525,7 +525,7 @@ struct LigatureSubtable if (unlikely (!componentData.sanitize (&c->sanitizer))) break; ligature_idx += componentData; - DEBUG_MSG (APPLY, nullptr, "Action store %u last %u", + DEBUG_MSG (APPLY, nullptr, "Action store %d last %d", bool (action & LigActionStore), bool (action & LigActionLast)); if (action & (LigActionStore | LigActionLast)) @@ -577,7 +577,7 @@ struct LigatureSubtable driver_context_t dc (this, c); StateTableDriver driver (machine, c->buffer, c->face); - driver.drive (&dc); + driver.drive (&dc, c); return_trace (dc.ret); } @@ -618,8 +618,27 @@ struct NoncontextualSubtable hb_glyph_info_t *info = c->buffer->info; unsigned int count = c->buffer->len; + // If there's only one range, we already checked the flag. + auto *last_range = c->range_flags && (c->range_flags->length > 1) ? &(*c->range_flags)[0] : nullptr; for (unsigned int i = 0; i < count; i++) { + /* This block copied from StateTableDriver::drive. Keep in sync. */ + if (last_range) + { + auto *range = last_range; + { + unsigned cluster = info[i].cluster; + while (cluster < range->cluster_first) + range--; + while (cluster > range->cluster_last) + range++; + + last_range = range; + } + if (!(range->flags & c->subtable_flags)) + continue; + } + const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs); if (replacement) { @@ -820,7 +839,7 @@ struct InsertionSubtable driver_context_t dc (this, c); StateTableDriver driver (machine, c->buffer, c->face); - driver.drive (&dc); + driver.drive (&dc, c); return_trace (dc.ret); } @@ -968,7 +987,7 @@ struct Chain // Check whether this type/setting pair was requested in the map, and if so, apply its flags. // (The search here only looks at the type and setting fields of feature_info_t.) hb_aat_map_builder_t::feature_info_t info = { type, setting, false, 0 }; - if (map->features.bsearch (info)) + if (map->current_features.bsearch (info)) { flags &= feature.disableFlags; flags |= feature.enableFlags; @@ -980,13 +999,21 @@ struct Chain setting = HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS; goto retry; } +#ifndef HB_NO_AAT + else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE && setting && + /* TODO: Rudimentary language matching. */ + hb_language_matches (map->face->table.ltag->get_language (setting - 1), map->props.language)) + { + flags &= feature.disableFlags; + flags |= feature.enableFlags; + } +#endif } } return flags; } - void apply (hb_aat_apply_context_t *c, - hb_mask_t flags) const + void apply (hb_aat_apply_context_t *c) const { const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount)); unsigned int count = subtableCount; @@ -994,8 +1021,10 @@ struct Chain { bool reverse; - if (!(subtable->subFeatureFlags & flags)) + if (hb_none (hb_iter (c->range_flags) | + hb_map ([&subtable] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable->subFeatureFlags & (_.flags); }))) goto skip; + c->subtable_flags = subtable->subFeatureFlags; if (!(subtable->get_coverage() & ChainSubtable::AllDirections) && HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != @@ -1034,7 +1063,7 @@ struct Chain bool (subtable->get_coverage () & ChainSubtable::Backwards) != HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); - if (!c->buffer->message (c->font, "start chainsubtable %d", c->lookup_index)) + if (!c->buffer->message (c->font, "start chainsubtable %u", c->lookup_index)) goto skip; if (reverse) @@ -1045,7 +1074,7 @@ struct Chain if (reverse) c->buffer->reverse (); - (void) c->buffer->message (c->font, "end chainsubtable %d", c->lookup_index); + (void) c->buffer->message (c->font, "end chainsubtable %u", c->lookup_index); if (unlikely (!c->buffer->successful)) return; @@ -1111,22 +1140,31 @@ struct mortmorx { const Chain *chain = &firstChain; unsigned int count = chainCount; + if (unlikely (!map->chain_flags.resize (count))) + return; for (unsigned int i = 0; i < count; i++) { - map->chain_flags.push (chain->compile_flags (mapper)); + map->chain_flags[i].push (hb_aat_map_t::range_flags_t {chain->compile_flags (mapper), + mapper->range_first, + mapper->range_last}); chain = &StructAfter> (*chain); } } - void apply (hb_aat_apply_context_t *c) const + void apply (hb_aat_apply_context_t *c, + const hb_aat_map_t &map) const { if (unlikely (!c->buffer->successful)) return; + + c->buffer->unsafe_to_concat (); + c->set_lookup_index (0); const Chain *chain = &firstChain; unsigned int count = chainCount; for (unsigned int i = 0; i < count; i++) { - chain->apply (c, c->plan->aat_map.chain_flags[i]); + c->range_flags = &map.chain_flags[i]; + chain->apply (c); if (unlikely (!c->buffer->successful)) return; chain = &StructAfter> (*chain); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh index 3054c76b96b..959382f356f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh @@ -42,7 +42,7 @@ struct OpticalBounds bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); + return_trace (c->check_struct (this)); } FWORD leftSide; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh index d99a53b14c4..cb531283498 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh @@ -62,7 +62,7 @@ struct TrackTableEntry } protected: - HBFixed track; /* Track value for this record. */ + F16DOT16 track; /* Track value for this record. */ NameID trackNameID; /* The 'name' table index for this track. * (a short word or phrase like "loose" * or "very tight") */ @@ -82,7 +82,7 @@ struct TrackData const void *base) const { unsigned int sizes = nSizes; - hb_array_t size_table ((base+sizeTable).arrayZ, sizes); + hb_array_t size_table ((base+sizeTable).arrayZ, sizes); float s0 = size_table[idx].to_float (); float s1 = size_table[idx + 1].to_float (); @@ -120,7 +120,7 @@ struct TrackData if (!sizes) return 0.; if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes); - hb_array_t size_table ((base+sizeTable).arrayZ, sizes); + hb_array_t size_table ((base+sizeTable).arrayZ, sizes); unsigned int size_index; for (size_index = 0; size_index < sizes - 1; size_index++) if (size_table[size_index].to_float () >= ptem) @@ -141,7 +141,7 @@ struct TrackData protected: HBUINT16 nTracks; /* Number of separate tracks included in this table. */ HBUINT16 nSizes; /* Number of point sizes included in this table. */ - NNOffset32To> + NNOffset32To> sizeTable; /* Offset from start of the tracking table to * Array[nSizes] of size values.. */ UnsizedArrayOf diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc index a9a9ce6b326..b0afbdfbb04 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc @@ -131,6 +131,7 @@ static const hb_aat_feature_mapping_t feature_mappings[] = {HB_TAG ('p','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS, (hb_aat_layout_feature_selector_t) 4}, {HB_TAG ('p','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, {HB_TAG ('q','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('r','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF}, {HB_TAG ('r','u','b','y'), HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF}, {HB_TAG ('s','i','n','f'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, {HB_TAG ('s','m','c','p'), HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE}, @@ -229,7 +230,7 @@ hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper, * * Note: does not examine the `GSUB` table. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 2.3.0 */ @@ -243,15 +244,23 @@ hb_aat_layout_has_substitution (hb_face_t *face) void hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, - hb_buffer_t *buffer) + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned num_features) { + hb_aat_map_builder_t builder (font->face, plan->props); + for (unsigned i = 0; i < num_features; i++) + builder.add_feature (features[i]); + hb_aat_map_t map; + builder.compile (map); + hb_blob_t *morx_blob = font->face->table.morx.get_blob (); const AAT::morx& morx = *morx_blob->as (); if (morx.has_data ()) { AAT::hb_aat_apply_context_t c (plan, font, buffer, morx_blob); if (!buffer->message (font, "start table morx")) return; - morx.apply (&c); + morx.apply (&c, map); (void) buffer->message (font, "end table morx"); return; } @@ -262,7 +271,7 @@ hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, { AAT::hb_aat_apply_context_t c (plan, font, buffer, mort_blob); if (!buffer->message (font, "start table mort")) return; - mort.apply (&c); + mort.apply (&c, map); (void) buffer->message (font, "end table mort"); return; } @@ -288,7 +297,7 @@ is_deleted_glyph (const hb_glyph_info_t *info) void hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer) { - hb_ot_layout_delete_glyphs_inplace (buffer, is_deleted_glyph); + buffer->delete_glyphs_inplace (is_deleted_glyph); } /** @@ -300,7 +309,7 @@ hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer) * * Note: does not examine the `GPOS` table. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 2.3.0 */ @@ -333,7 +342,7 @@ hb_aat_layout_position (const hb_ot_shape_plan_t *plan, * Tests whether the specified face includes any tracking information * in the `trak` table. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 2.3.0 */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh index 1a95507ccee..36bb0ef07dd 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh @@ -53,7 +53,9 @@ hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper, HB_INTERNAL void hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, - hb_buffer_t *buffer); + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned num_features); HB_INTERNAL void hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc index ad3eff7935f..122f29dc681 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc @@ -36,27 +36,29 @@ #include "hb-aat-layout-feat-table.hh" -void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value) +void hb_aat_map_builder_t::add_feature (const hb_feature_t &feature) { if (!face->table.feat->has_data ()) return; - if (tag == HB_TAG ('a','a','l','t')) + if (feature.tag == HB_TAG ('a','a','l','t')) { if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES)) return; - feature_info_t *info = features.push(); - info->type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES; - info->setting = (hb_aat_layout_feature_selector_t) value; - info->seq = features.length; - info->is_exclusive = true; + feature_range_t *range = features.push(); + range->start = feature.start; + range->end = feature.end; + range->info.type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES; + range->info.setting = (hb_aat_layout_feature_selector_t) feature.value; + range->info.seq = features.length; + range->info.is_exclusive = true; return; } - const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (tag); + const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (feature.tag); if (!mapping) return; - const AAT::FeatureName* feature = &face->table.feat->get_feature (mapping->aatFeatureType); - if (!feature->has_data ()) + const AAT::FeatureName* feature_name = &face->table.feat->get_feature (mapping->aatFeatureType); + if (!feature_name->has_data ()) { /* Special case: Chain::compile_flags will fall back to the deprecated version of * small-caps if necessary, so we need to check for that possibility. @@ -64,38 +66,106 @@ void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value) if (mapping->aatFeatureType == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE && mapping->selectorToEnable == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS) { - feature = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE); - if (!feature->has_data ()) return; + feature_name = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE); + if (!feature_name->has_data ()) return; } else return; } - feature_info_t *info = features.push(); - info->type = mapping->aatFeatureType; - info->setting = value ? mapping->selectorToEnable : mapping->selectorToDisable; - info->seq = features.length; - info->is_exclusive = feature->is_exclusive (); + feature_range_t *range = features.push(); + range->start = feature.start; + range->end = feature.end; + range->info.type = mapping->aatFeatureType; + range->info.setting = feature.value ? mapping->selectorToEnable : mapping->selectorToDisable; + range->info.seq = features.length; + range->info.is_exclusive = feature_name->is_exclusive (); } void hb_aat_map_builder_t::compile (hb_aat_map_t &m) { - /* Sort features and merge duplicates */ - if (features.length) + /* Compute active features per range, and compile each. */ + + /* Sort features by start/end events. */ + hb_vector_t feature_events; + for (unsigned int i = 0; i < features.length; i++) + { + auto &feature = features[i]; + + if (features[i].start == features[i].end) + continue; + + feature_event_t *event; + + event = feature_events.push (); + event->index = features[i].start; + event->start = true; + event->feature = feature.info; + + event = feature_events.push (); + event->index = features[i].end; + event->start = false; + event->feature = feature.info; + } + feature_events.qsort (); + /* Add a strategic final event. */ + { + feature_info_t feature; + feature.seq = features.length + 1; + + feature_event_t *event = feature_events.push (); + event->index = -1; /* This value does magic. */ + event->start = false; + event->feature = feature; + } + + /* Scan events and save features for each range. */ + hb_sorted_vector_t active_features; + unsigned int last_index = 0; + for (unsigned int i = 0; i < feature_events.length; i++) { - features.qsort (); - unsigned int j = 0; - for (unsigned int i = 1; i < features.length; i++) - if (features[i].type != features[j].type || - /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off - * respectively, so we mask out the low-order bit when checking for "duplicates" - * (selectors referring to the same feature setting) here. */ - (!features[i].is_exclusive && ((features[i].setting & ~1) != (features[j].setting & ~1)))) - features[++j] = features[i]; - features.shrink (j + 1); + feature_event_t *event = &feature_events[i]; + + if (event->index != last_index) + { + /* Save a snapshot of active features and the range. */ + + /* Sort features and merge duplicates */ + current_features = active_features; + range_first = last_index; + range_last = event->index - 1; + if (current_features.length) + { + current_features.qsort (); + unsigned int j = 0; + for (unsigned int i = 1; i < current_features.length; i++) + if (current_features[i].type != current_features[j].type || + /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off + * respectively, so we mask out the low-order bit when checking for "duplicates" + * (selectors referring to the same feature setting) here. */ + (!current_features[i].is_exclusive && ((current_features[i].setting & ~1) != (current_features[j].setting & ~1)))) + current_features[++j] = current_features[i]; + current_features.shrink (j + 1); + } + + hb_aat_layout_compile_map (this, &m); + + last_index = event->index; + } + + if (event->start) + { + active_features.push (event->feature); + } else { + feature_info_t *feature = active_features.lsearch (event->feature); + if (feature) + active_features.remove_ordered (feature - active_features.arrayZ); + } } - hb_aat_layout_compile_map (this, &m); + for (auto &chain_flags : m.chain_flags) + // With our above setup this value is one less than desired; adjust it. + chain_flags.tail().cluster_last = HB_FEATURE_GLOBAL_END; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh index ce30daa6084..b21f5bacb9e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh @@ -35,16 +35,15 @@ struct hb_aat_map_t friend struct hb_aat_map_builder_t; public: - - void init () + struct range_flags_t { - memset (this, 0, sizeof (*this)); - chain_flags.init (); - } - void fini () { chain_flags.fini (); } + hb_mask_t flags; + unsigned cluster_first; + unsigned cluster_last; // end - 1 + }; public: - hb_vector_t chain_flags; + hb_vector_t> chain_flags; }; struct hb_aat_map_builder_t @@ -52,10 +51,11 @@ struct hb_aat_map_builder_t public: HB_INTERNAL hb_aat_map_builder_t (hb_face_t *face_, - const hb_segment_properties_t *props_ HB_UNUSED) : - face (face_) {} + const hb_segment_properties_t props_) : + face (face_), + props (props_) {} - HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value=1); + HB_INTERNAL void add_feature (const hb_feature_t &feature); HB_INTERNAL void compile (hb_aat_map_t &m); @@ -77,7 +77,7 @@ struct hb_aat_map_builder_t return (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0); } - /* compares type & setting only, not is_exclusive flag or seq number */ + /* compares type & setting only */ int cmp (const feature_info_t& f) const { return (f.type != type) ? (f.type < type ? -1 : 1) : @@ -85,11 +85,38 @@ struct hb_aat_map_builder_t } }; + struct feature_range_t + { + feature_info_t info; + unsigned start; + unsigned end; + }; + + private: + struct feature_event_t + { + unsigned int index; + bool start; + feature_info_t feature; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) { + const feature_event_t *a = (const feature_event_t *) pa; + const feature_event_t *b = (const feature_event_t *) pb; + return a->index < b->index ? -1 : a->index > b->index ? 1 : + a->start < b->start ? -1 : a->start > b->start ? 1 : + feature_info_t::cmp (&a->feature, &b->feature); + } + }; + public: hb_face_t *face; + hb_segment_properties_t props; public: - hb_sorted_vector_t features; + hb_sorted_vector_t features; + hb_sorted_vector_t current_features; + unsigned range_first = HB_FEATURE_GLOBAL_START; + unsigned range_last = HB_FEATURE_GLOBAL_END; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-algs.hh b/src/java.desktop/share/native/libharfbuzz/hb-algs.hh index 162601b380d..0080b380850 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-algs.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-algs.hh @@ -109,15 +109,16 @@ struct BEInt struct __attribute__((packed)) packed_uint16_t { uint16_t v; }; constexpr operator Type () const { -#if ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \ +#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \ + ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \ defined(__BYTE_ORDER) && \ (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN) /* Spoon-feed the compiler a big-endian integer with alignment 1. * https://github.com/harfbuzz/harfbuzz/pull/1398 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - return __builtin_bswap16 (((packed_uint16_t *) this)->v); + return __builtin_bswap16 (((packed_uint16_t *) v)->v); #else /* __BYTE_ORDER == __BIG_ENDIAN */ - return ((packed_uint16_t *) this)->v; + return ((packed_uint16_t *) v)->v; #endif #else return (v[0] << 8) @@ -153,15 +154,16 @@ struct BEInt struct __attribute__((packed)) packed_uint32_t { uint32_t v; }; constexpr operator Type () const { -#if ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \ +#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \ + ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \ defined(__BYTE_ORDER) && \ (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN) /* Spoon-feed the compiler a big-endian integer with alignment 1. * https://github.com/harfbuzz/harfbuzz/pull/1398 */ #if __BYTE_ORDER == __LITTLE_ENDIAN - return __builtin_bswap32 (((packed_uint32_t *) this)->v); + return __builtin_bswap32 (((packed_uint32_t *) v)->v); #else /* __BYTE_ORDER == __BIG_ENDIAN */ - return ((packed_uint32_t *) this)->v; + return ((packed_uint32_t *) v)->v; #endif #else return (v[0] << 24) @@ -234,17 +236,6 @@ struct template constexpr auto impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ()) - template constexpr uint32_t - impl (const hb::shared_ptr& v, hb_priority<1>) const - { - return v.get () ? v.get ()->hash () : 0; - } - template constexpr uint32_t - impl (const hb::unique_ptr& v, hb_priority<1>) const - { - return v.get () ? v.get ()->hash () : 0; - } - template constexpr auto impl (const T& v, hb_priority<0>) const HB_RETURN (uint32_t, std::hash>{} (hb_deref (v))) @@ -493,6 +484,17 @@ struct } HB_FUNCOBJ (hb_equal); +struct +{ + template void + operator () (T& a, T& b) const + { + using std::swap; // allow ADL + swap (a, b); + } +} +HB_FUNCOBJ (hb_swap); + template struct hb_pair_t @@ -505,7 +507,7 @@ struct hb_pair_t hb_enable_if (std::is_default_constructible::value && std::is_default_constructible::value)> hb_pair_t () : first (), second () {} - hb_pair_t (T1 a, T2 b) : first (a), second (b) {} + hb_pair_t (T1 a, T2 b) : first (std::forward (a)), second (std::forward (b)) {} template (const pair_t& o) const { return first > o.first || (first == o.first && second > o.second); } bool operator <= (const pair_t& o) const { return !(*this > o); } + static int cmp (const void *pa, const void *pb) + { + pair_t *a = (pair_t *) pa; + pair_t *b = (pair_t *) pb; + + if (a->first < b->first) return -1; + if (a->first > b->first) return +1; + if (a->second < b->second) return -1; + if (a->second > b->second) return +1; + return 0; + } + + friend void swap (hb_pair_t& a, hb_pair_t& b) + { + hb_swap (a.first, b.first); + hb_swap (a.second, b.second); + } + + T1 first; T2 second; }; -#define hb_pair_t(T1,T2) hb_pair_t template static inline hb_pair_t hb_pair (T1&& a, T2&& b) { return hb_pair_t (a, b); } @@ -551,14 +571,14 @@ struct { template constexpr auto operator () (T&& a, T2&& b) const HB_AUTO_RETURN - (a <= b ? std::forward (a) : std::forward (b)) + (a <= b ? a : b) } HB_FUNCOBJ (hb_min); struct { template constexpr auto operator () (T&& a, T2&& b) const HB_AUTO_RETURN - (a >= b ? std::forward (a) : std::forward (b)) + (a >= b ? a : b) } HB_FUNCOBJ (hb_max); struct @@ -569,17 +589,6 @@ struct } HB_FUNCOBJ (hb_clamp); -struct -{ - template void - operator () (T& a, T& b) const - { - using std::swap; // allow ADL - swap (a, b); - } -} -HB_FUNCOBJ (hb_swap); - /* * Bithacks. */ @@ -848,19 +857,14 @@ hb_in_range (T u, T lo, T hi) return (T)(u - lo) <= (T)(hi - lo); } template static inline bool -hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2) -{ - return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2); -} -template static inline bool -hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3) +hb_in_ranges (T u, T lo1, T hi1) { - return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3); + return hb_in_range (u, lo1, hi1); } -template static inline bool -hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3, T lo4, T hi4) +template static inline bool +hb_in_ranges (T u, T lo1, T hi1, Ts... ds) { - return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3) || hb_in_range (u, lo4, hi4); + return hb_in_range (u, lo1, hi1) || hb_in_ranges (u, ds...); } @@ -868,10 +872,19 @@ hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3, T lo4, T hi4) * Overflow checking. */ -/* Consider __builtin_mul_overflow use here also */ static inline bool -hb_unsigned_mul_overflows (unsigned int count, unsigned int size) +hb_unsigned_mul_overflows (unsigned int count, unsigned int size, unsigned *result = nullptr) { +/* avoid with xlc16 clang on AIX; it sets the gcc macros */ +#if (defined(__GNUC__) && !defined(AIX) && (__GNUC__ >= 4)) || (defined(__clang__) && (__clang_major__ >= 8)) + unsigned stack_result; + if (!result) + result = &stack_result; + return __builtin_mul_overflow (count, size, result); +#endif + + if (result) + *result = count * size; return (size > 0) && (count >= ((unsigned int) -1) / size); } @@ -972,7 +985,7 @@ void hb_qsort(void *base, size_t nel, size_t width, [void *arg]); */ -#define SORT_R_SWAP(a,b,tmp) ((tmp) = (a), (a) = (b), (b) = (tmp)) +#define SORT_R_SWAP(a,b,tmp) ((void) ((tmp) = (a)), (void) ((a) = (b)), (b) = (tmp)) /* swap a and b */ /* a and b must not be equal! */ @@ -1163,9 +1176,12 @@ hb_qsort (void *base, size_t nel, size_t width, } -template static inline void -hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2) +template static inline void +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2 = nullptr) { + static_assert (hb_is_trivially_copy_assignable (T), ""); + static_assert (hb_is_trivially_copy_assignable (T3), ""); + for (unsigned int i = 1; i < len; i++) { unsigned int j = i; @@ -1188,12 +1204,6 @@ hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *) } } -template static inline void -hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) -{ - hb_stable_sort (array, len, compar, (int *) nullptr); -} - static inline hb_bool_t hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out) { @@ -1321,47 +1331,4 @@ struct HB_FUNCOBJ (hb_dec); -/* Compiler-assisted vectorization. */ - -/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))), - * basically a fixed-size bitset. */ -template -struct hb_vector_size_t -{ - elt_t& operator [] (unsigned int i) { return v[i]; } - const elt_t& operator [] (unsigned int i) const { return v[i]; } - - void clear (unsigned char v = 0) { memset (this, v, sizeof (*this)); } - - template - hb_vector_size_t process (const Op& op) const - { - hb_vector_size_t r; - for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) - r.v[i] = op (v[i]); - return r; - } - template - hb_vector_size_t process (const Op& op, const hb_vector_size_t &o) const - { - hb_vector_size_t r; - for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) - r.v[i] = op (v[i], o.v[i]); - return r; - } - hb_vector_size_t operator | (const hb_vector_size_t &o) const - { return process (hb_bitwise_or, o); } - hb_vector_size_t operator & (const hb_vector_size_t &o) const - { return process (hb_bitwise_and, o); } - hb_vector_size_t operator ^ (const hb_vector_size_t &o) const - { return process (hb_bitwise_xor, o); } - hb_vector_size_t operator ~ () const - { return process (hb_bitwise_neg); } - - private: - static_assert (0 == byte_size % sizeof (elt_t), ""); - elt_t v[byte_size / sizeof (elt_t)]; -}; - - #endif /* HB_ALGS_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-array.hh b/src/java.desktop/share/native/libharfbuzz/hb-array.hh index 0a92392d319..08b25987061 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-array.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-array.hh @@ -100,10 +100,17 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&> /* Ouch. The operator== compares the contents of the array. For range-based for loops, * it's best if we can just compare arrayZ, though comparing contents is still fast, * but also would require that Type has operator==. As such, we optimize this operator - * for range-based for loop and just compare arrayZ. No need to compare length, as we - * assume we're only compared to .end(). */ + * for range-based for loop and just compare arrayZ and length. + * + * The above comment is outdated now because we implemented separate begin/end to + * objects that were using hb_array_t for range-based loop before. */ bool operator != (const hb_array_t& o) const - { return arrayZ != o.arrayZ; } + { return this->arrayZ != o.arrayZ || this->length != o.length; } + + /* Faster range-based for loop without bounds-check. */ + Type *begin () const { return arrayZ; } + Type *end () const { return arrayZ + length; } + /* Extra operators. */ @@ -113,11 +120,11 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&> HB_INTERNAL bool operator == (const hb_array_t &o) const; - uint32_t hash () const { + uint32_t hash () const + { uint32_t current = 0; - for (unsigned int i = 0; i < this->length; i++) { - current = current * 31 + hb_hash (this->arrayZ[i]); - } + for (auto &v : *this) + current = current * 31 + hb_hash (v); return current; } @@ -185,23 +192,18 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&> hb_sorted_array_t qsort (int (*cmp_)(const void*, const void*)) { + //static_assert (hb_enable_if (hb_is_trivially_copy_assignable(Type)), ""); if (likely (length)) hb_qsort (arrayZ, length, this->get_item_size (), cmp_); return hb_sorted_array_t (*this); } hb_sorted_array_t qsort () { + //static_assert (hb_enable_if (hb_is_trivially_copy_assignable(Type)), ""); if (likely (length)) hb_qsort (arrayZ, length, this->get_item_size (), Type::cmp); return hb_sorted_array_t (*this); } - void qsort (unsigned int start, unsigned int end) - { - end = hb_min (end, length); - assert (start <= end); - if (likely (start < end)) - hb_qsort (arrayZ + start, end - start, this->get_item_size (), Type::cmp); - } /* * Other methods. @@ -220,11 +222,8 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&> if (end < start + 2) return; - for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--) { - Type temp = arrayZ[rhs]; - arrayZ[rhs] = arrayZ[lhs]; - arrayZ[lhs] = temp; - } + for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--) + hb_swap (arrayZ[rhs], arrayZ[lhs]); } hb_array_t sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const @@ -266,17 +265,31 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&> void fini () { hb_free ((void *) arrayZ); arrayZ = nullptr; length = 0; } - template + template )))> hb_array_t copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); auto* out = c->start_embed (arrayZ); - if (unlikely (!c->extend_size (out, get_size ()))) return_trace (hb_array_t ()); + if (unlikely (!c->extend_size (out, get_size (), false))) return_trace (hb_array_t ()); for (unsigned i = 0; i < length; i++) out[i] = arrayZ[i]; /* TODO: add version that calls c->copy() */ return_trace (hb_array_t (out, length)); } + template ))> + hb_array_t copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto* out = c->start_embed (arrayZ); + if (unlikely (!c->extend_size (out, get_size (), false))) return_trace (hb_array_t ()); + hb_memcpy (out, arrayZ, get_size ()); + return_trace (hb_array_t (out, length)); + } + template bool sanitize (hb_sanitize_context_t *c) const { return c->check_array (arrayZ, length); } @@ -291,6 +304,9 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&> unsigned int backwards_length = 0; }; template inline hb_array_t +hb_array () +{ return hb_array_t (); } +template inline hb_array_t hb_array (T *array, unsigned int length) { return hb_array_t (array, length); } template inline hb_array_t @@ -299,8 +315,8 @@ hb_array (T (&array_)[length_]) template struct hb_sorted_array_t : - hb_iter_t, Type&>, - hb_array_t + hb_array_t, + hb_iter_t, Type&> { typedef hb_iter_t iter_base_t; HB_ITER_USING (iter_base_t); @@ -320,17 +336,24 @@ struct hb_sorted_array_t : template constexpr hb_sorted_array_t (const hb_array_t &o) : - hb_iter_t (), - hb_array_t (o) {} + hb_array_t (o), + hb_iter_t () {} template hb_sorted_array_t& operator = (const hb_array_t &o) { hb_array_t (*this) = o; return *this; } /* Iterator implementation. */ + + /* See comment in hb_array_of::operator != */ bool operator != (const hb_sorted_array_t& o) const { return this->arrayZ != o.arrayZ || this->length != o.length; } + /* Faster range-based for loop without bounds-check. */ + Type *begin () const { return this->arrayZ; } + Type *end () const { return this->arrayZ + this->length; } + + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const { return hb_sorted_array_t (((const hb_array_t *) (this))->sub_array (start_offset, seg_count)); } hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const @@ -344,7 +367,7 @@ struct hb_sorted_array_t : unsigned int i; return bfind (x, &i) ? &this->arrayZ[i] : not_found; } - template + template const Type *bsearch (const T &x, const Type *not_found = nullptr) const { unsigned int i; @@ -391,7 +414,7 @@ struct hb_sorted_array_t : this->length, sizeof (Type), _hb_cmp_method, - ds...); + std::forward (ds)...); } }; template inline hb_sorted_array_t @@ -423,18 +446,42 @@ inline bool hb_array_t::operator == (const hb_array_t -inline uint32_t hb_array_t::hash () const { +inline uint32_t hb_array_t::hash () const +{ uint32_t current = 0; - for (unsigned int i = 0; i < this->length; i++) - current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u); + unsigned i = 0; + +#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \ + ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) + struct __attribute__((packed)) packed_uint32_t { uint32_t v; }; + for (; i + 4 <= this->length; i += 4) + current = current * 31 + hb_hash ((uint32_t) ((packed_uint32_t *) &this->arrayZ[i])->v); +#endif + + for (; i < this->length; i++) + current = current * 31 + hb_hash (this->arrayZ[i]); return current; } + template <> -inline uint32_t hb_array_t::hash () const { +inline uint32_t hb_array_t::hash () const +{ uint32_t current = 0; - for (unsigned int i = 0; i < this->length; i++) - current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u); + unsigned i = 0; + +#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \ + ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) + struct __attribute__((packed)) packed_uint32_t { uint32_t v; }; + for (; i + 4 <= this->length; i += 4) + current = current * 31 + hb_hash ((uint32_t) ((packed_uint32_t *) &this->arrayZ[i])->v); +#endif + + for (; i < this->length; i++) + current = current * 31 + hb_hash (this->arrayZ[i]); return current; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh index 1267bc4fe20..57e94761e80 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh @@ -84,11 +84,11 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) #define _hb_memory_r_barrier() std::atomic_thread_fence(std::memory_order_acquire) #define _hb_memory_w_barrier() std::atomic_thread_fence(std::memory_order_release) -#define hb_atomic_int_impl_add(AI, V) (reinterpret_cast *> (AI)->fetch_add ((V), std::memory_order_acq_rel)) -#define hb_atomic_int_impl_set_relaxed(AI, V) (reinterpret_cast *> (AI)->store ((V), std::memory_order_relaxed)) -#define hb_atomic_int_impl_set(AI, V) (reinterpret_cast *> (AI)->store ((V), std::memory_order_release)) -#define hb_atomic_int_impl_get_relaxed(AI) (reinterpret_cast const *> (AI)->load (std::memory_order_relaxed)) -#define hb_atomic_int_impl_get(AI) (reinterpret_cast const *> (AI)->load (std::memory_order_acquire)) +#define hb_atomic_int_impl_add(AI, V) (reinterpret_cast::type> *> (AI)->fetch_add ((V), std::memory_order_acq_rel)) +#define hb_atomic_int_impl_set_relaxed(AI, V) (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_relaxed)) +#define hb_atomic_int_impl_set(AI, V) (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_release)) +#define hb_atomic_int_impl_get_relaxed(AI) (reinterpret_cast::type> const *> (AI)->load (std::memory_order_relaxed)) +#define hb_atomic_int_impl_get(AI) (reinterpret_cast::type> const *> (AI)->load (std::memory_order_acquire)) #define hb_atomic_ptr_impl_set_relaxed(P, V) (reinterpret_cast *> (P)->store ((V), std::memory_order_relaxed)) #define hb_atomic_ptr_impl_get_relaxed(P) (reinterpret_cast const *> (P)->load (std::memory_order_relaxed)) @@ -111,6 +111,24 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) #endif +/* This should never be disabled, even under HB_NO_MT. + * except that MSVC gives me an internal compiler error, so disabled there. + * + * https://github.com/harfbuzz/harfbuzz/pull/4119 + */ +#ifndef _hb_compiler_memory_r_barrier +#if defined(__ATOMIC_ACQUIRE) // gcc-like +#define _hb_compiler_memory_r_barrier() asm volatile("": : :"memory") +#elif !defined(_MSC_VER) +#include +#define _hb_compiler_memory_r_barrier() std::atomic_signal_fence (std::memory_order_acquire) +#else +#define _hb_compiler_memory_r_barrier() do {} while (0) +#endif +#endif + + + #ifndef _hb_memory_r_barrier #define _hb_memory_r_barrier() _hb_memory_barrier () #endif @@ -132,24 +150,47 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) #endif #ifndef hb_atomic_int_impl_set inline void hb_atomic_int_impl_set (int *AI, int v) { _hb_memory_w_barrier (); *AI = v; } +inline void hb_atomic_int_impl_set (short *AI, short v) { _hb_memory_w_barrier (); *AI = v; } #endif #ifndef hb_atomic_int_impl_get inline int hb_atomic_int_impl_get (const int *AI) { int v = *AI; _hb_memory_r_barrier (); return v; } +inline short hb_atomic_int_impl_get (const short *AI) { short v = *AI; _hb_memory_r_barrier (); return v; } #endif #ifndef hb_atomic_ptr_impl_get inline void *hb_atomic_ptr_impl_get (void ** const P) { void *v = *P; _hb_memory_r_barrier (); return v; } #endif +struct hb_atomic_short_t +{ + hb_atomic_short_t () = default; + constexpr hb_atomic_short_t (short v) : v (v) {} + + hb_atomic_short_t& operator = (short v_) { set_relaxed (v_); return *this; } + operator short () const { return get_relaxed (); } + + void set_relaxed (short v_) { hb_atomic_int_impl_set_relaxed (&v, v_); } + void set_release (short v_) { hb_atomic_int_impl_set (&v, v_); } + short get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); } + short get_acquire () const { return hb_atomic_int_impl_get (&v); } + short inc () { return hb_atomic_int_impl_add (&v, 1); } + short dec () { return hb_atomic_int_impl_add (&v, -1); } + + short v = 0; +}; + struct hb_atomic_int_t { hb_atomic_int_t () = default; constexpr hb_atomic_int_t (int v) : v (v) {} + hb_atomic_int_t& operator = (int v_) { set_relaxed (v_); return *this; } + operator int () const { return get_relaxed (); } + void set_relaxed (int v_) { hb_atomic_int_impl_set_relaxed (&v, v_); } - void set (int v_) { hb_atomic_int_impl_set (&v, v_); } + void set_release (int v_) { hb_atomic_int_impl_set (&v, v_); } int get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); } - int get () const { return hb_atomic_int_impl_get (&v); } + int get_acquire () const { return hb_atomic_int_impl_get (&v); } int inc () { return hb_atomic_int_impl_add (&v, 1); } int dec () { return hb_atomic_int_impl_add (&v, -1); } @@ -167,11 +208,11 @@ struct hb_atomic_ptr_t void init (T* v_ = nullptr) { set_relaxed (v_); } void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); } T *get_relaxed () const { return (T *) hb_atomic_ptr_impl_get_relaxed (&v); } - T *get () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); } + T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); } bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); } - T * operator -> () const { return get (); } - template operator C * () const { return get (); } + T * operator -> () const { return get_acquire (); } + template operator C * () const { return get_acquire (); } T *v = nullptr; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh b/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh index 5629ddc4cea..81e2a4997bd 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh @@ -30,32 +30,89 @@ #include "hb.hh" + +/* Compiler-assisted vectorization. */ + +/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))), + * basically a fixed-size bitset. We can't use the compiler type because hb_vector_t cannot + * guarantee alignment requirements. */ +template +struct hb_vector_size_t +{ + elt_t& operator [] (unsigned int i) { return v[i]; } + const elt_t& operator [] (unsigned int i) const { return v[i]; } + + void init0 () + { + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + v[i] = 0; + } + void init1 () + { + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + v[i] = (elt_t) -1; + } + + template + hb_vector_size_t process (const Op& op) const + { + hb_vector_size_t r; + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + r.v[i] = op (v[i]); + return r; + } + template + hb_vector_size_t process (const Op& op, const hb_vector_size_t &o) const + { + hb_vector_size_t r; + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + r.v[i] = op (v[i], o.v[i]); + return r; + } + hb_vector_size_t operator | (const hb_vector_size_t &o) const + { return process (hb_bitwise_or, o); } + hb_vector_size_t operator & (const hb_vector_size_t &o) const + { return process (hb_bitwise_and, o); } + hb_vector_size_t operator ^ (const hb_vector_size_t &o) const + { return process (hb_bitwise_xor, o); } + hb_vector_size_t operator ~ () const + { return process (hb_bitwise_neg); } + + hb_array_t iter () const + { return hb_array (v); } + + private: + static_assert (0 == byte_size % sizeof (elt_t), ""); + elt_t v[byte_size / sizeof (elt_t)]; +}; + + struct hb_bit_page_t { - void init0 () { v.clear (); } - void init1 () { v.clear (0xFF); } + void init0 () { v.init0 (); } + void init1 () { v.init1 (); } - constexpr unsigned len () const + static inline constexpr unsigned len () { return ARRAY_LENGTH_CONST (v); } bool is_empty () const { - for (unsigned i = 0; i < len (); i++) - if (v[i]) - return false; - return true; + return + + hb_iter (v) + | hb_none + ; } uint32_t hash () const { - uint32_t h = 0; - for (unsigned i = 0; i < len (); i++) - h = h * 31 + hb_hash (v[i]); - return h; + return + + hb_iter (v) + | hb_reduce ([] (uint32_t h, const elt_t &_) { return h * 31 + hb_hash (_); }, (uint32_t) 0u) + ; } void add (hb_codepoint_t g) { elt (g) |= mask (g); } void del (hb_codepoint_t g) { elt (g) &= ~mask (g); } - void set (hb_codepoint_t g, bool v) { if (v) add (g); else del (g); } + void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); } bool get (hb_codepoint_t g) const { return elt (g) & mask (g); } void add_range (hb_codepoint_t a, hb_codepoint_t b) @@ -69,7 +126,7 @@ struct hb_bit_page_t *la |= ~(mask (a) - 1); la++; - memset (la, 0xff, (char *) lb - (char *) la); + hb_memset (la, 0xff, (char *) lb - (char *) la); *lb |= ((mask (b) << 1) - 1); } @@ -85,7 +142,7 @@ struct hb_bit_page_t *la &= mask (a) - 1; la++; - memset (la, 0, (char *) lb - (char *) la); + hb_memset (la, 0, (char *) lb - (char *) la); *lb &= ~((mask (b) << 1) - 1); } @@ -101,13 +158,13 @@ struct hb_bit_page_t hb_codepoint_t *p, unsigned int size) const { - unsigned int start_v = start_value >> ELT_BITS_LOG_2; + unsigned int start_v = start_value / ELT_BITS; unsigned int start_bit = start_value & ELT_MASK; unsigned int count = 0; for (unsigned i = start_v; i < len () && count < size; i++) { elt_t bits = v[i]; - uint32_t v_base = base | (i << ELT_BITS_LOG_2); + uint32_t v_base = base | (i * ELT_BITS); for (unsigned int j = start_bit; j < ELT_BITS && count < size; j++) { if ((elt_t(1) << j) & bits) { @@ -132,13 +189,13 @@ struct hb_bit_page_t unsigned int size, hb_codepoint_t *next_value) const { - unsigned int start_v = start_value >> ELT_BITS_LOG_2; + unsigned int start_v = start_value / ELT_BITS; unsigned int start_bit = start_value & ELT_MASK; unsigned int count = 0; for (unsigned i = start_v; i < len () && count < size; i++) { elt_t bits = v[i]; - uint32_t v_offset = i << ELT_BITS_LOG_2; + uint32_t v_offset = i * ELT_BITS; for (unsigned int j = start_bit; j < ELT_BITS && count < size; j++) { if ((elt_t(1) << j) & bits) @@ -161,7 +218,10 @@ struct hb_bit_page_t bool is_equal (const hb_bit_page_t &other) const { - return 0 == hb_memcmp (&v, &other.v, sizeof (v)); + for (unsigned i = 0; i < len (); i++) + if (v[i] != other.v[i]) + return false; + return true; } bool is_subset (const hb_bit_page_t &larger_page) const { @@ -173,10 +233,10 @@ struct hb_bit_page_t unsigned int get_population () const { - unsigned int pop = 0; - for (unsigned int i = 0; i < len (); i++) - pop += hb_popcount (v[i]); - return pop; + return + + hb_iter (v) + | hb_reduce ([] (unsigned pop, const elt_t &_) { return pop + hb_popcount (_); }, 0u) + ; } bool next (hb_codepoint_t *codepoint) const @@ -250,10 +310,10 @@ struct hb_bit_page_t static constexpr hb_codepoint_t INVALID = HB_SET_VALUE_INVALID; typedef unsigned long long elt_t; - static constexpr unsigned PAGE_BITS = 512; - static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, ""); - static constexpr unsigned PAGE_BITS_LOG_2 = 9; + static constexpr unsigned PAGE_BITS_LOG_2 = 9; // 512 bits + static constexpr unsigned PAGE_BITS = 1 << PAGE_BITS_LOG_2; static_assert (1 << PAGE_BITS_LOG_2 == PAGE_BITS, ""); + static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, ""); static constexpr unsigned PAGE_BITMASK = PAGE_BITS - 1; static unsigned int elt_get_min (const elt_t &elt) { return hb_ctz (elt); } @@ -262,8 +322,6 @@ struct hb_bit_page_t typedef hb_vector_size_t vector_t; static constexpr unsigned ELT_BITS = sizeof (elt_t) * 8; - static constexpr unsigned ELT_BITS_LOG_2 = 6; - static_assert (1 << ELT_BITS_LOG_2 == ELT_BITS, ""); static constexpr unsigned ELT_MASK = ELT_BITS - 1; static constexpr unsigned BITS = sizeof (vector_t) * 8; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh b/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh index 9562ca36a32..bf5a0b446de 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh @@ -74,6 +74,11 @@ struct hb_bit_set_invertible_t inverted = !inverted; } + bool is_inverted () const + { + return inverted; + } + bool is_empty () const { hb_codepoint_t v = INVALID; @@ -123,10 +128,8 @@ struct hb_bit_set_invertible_t bool get (hb_codepoint_t g) const { return s.get (g) ^ inverted; } /* Has interface. */ - static constexpr bool SENTINEL = false; - typedef bool value_t; - value_t operator [] (hb_codepoint_t k) const { return get (k); } - bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + bool operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k]; } /* Predicate. */ bool operator () (hb_codepoint_t k) const { return has (k); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh b/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh index be244835a15..337bbe6b632 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh @@ -38,7 +38,7 @@ struct hb_bit_set_t hb_bit_set_t () = default; ~hb_bit_set_t () = default; - hb_bit_set_t (const hb_bit_set_t& other) : hb_bit_set_t () { set (other); } + hb_bit_set_t (const hb_bit_set_t& other) : hb_bit_set_t () { set (other, true); } hb_bit_set_t ( hb_bit_set_t&& other) : hb_bit_set_t () { hb_swap (*this, other); } hb_bit_set_t& operator= (const hb_bit_set_t& other) { set (other); return *this; } hb_bit_set_t& operator= (hb_bit_set_t&& other) { hb_swap (*this, other); return *this; } @@ -56,7 +56,7 @@ struct hb_bit_set_t { successful = true; population = 0; - last_page_lookup.set_relaxed (0); + last_page_lookup = 0; page_map.init (); pages.init (); } @@ -85,12 +85,16 @@ struct hb_bit_set_t void err () { if (successful) successful = false; } /* TODO Remove */ bool in_error () const { return !successful; } - bool resize (unsigned int count) + bool resize (unsigned int count, bool clear = true, bool exact_size = false) { if (unlikely (!successful)) return false; - if (unlikely (!pages.resize (count) || !page_map.resize (count))) + + if (pages.length == 0 && count == 1) + exact_size = true; // Most sets are small and local + + if (unlikely (!pages.resize (count, clear, exact_size) || !page_map.resize (count, clear, exact_size))) { - pages.resize (page_map.length); + pages.resize (page_map.length, clear, exact_size); successful = false; return false; } @@ -330,10 +334,8 @@ struct hb_bit_set_t } /* Has interface. */ - static constexpr bool SENTINEL = false; - typedef bool value_t; - value_t operator [] (hb_codepoint_t k) const { return get (k); } - bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + bool operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k]; } /* Predicate. */ bool operator () (hb_codepoint_t k) const { return has (k); } @@ -348,11 +350,11 @@ struct hb_bit_set_t hb_codepoint_t c = first - 1; return next (&c) && c <= last; } - void set (const hb_bit_set_t &other) + void set (const hb_bit_set_t &other, bool exact_size = false) { if (unlikely (!successful)) return; unsigned int count = other.pages.length; - if (unlikely (!resize (count))) + if (unlikely (!resize (count, false, exact_size))) return; population = other.population; @@ -391,7 +393,7 @@ struct hb_bit_set_t bool is_subset (const hb_bit_set_t &larger_set) const { if (has_population () && larger_set.has_population () && - population != larger_set.population) + population > larger_set.population) return false; uint32_t spi = 0; @@ -424,7 +426,7 @@ struct hb_bit_set_t private: bool allocate_compact_workspace (hb_vector_t& workspace) { - if (unlikely (!workspace.resize (pages.length))) + if (unlikely (!workspace.resize_exact (pages.length))) { successful = false; return false; @@ -465,12 +467,10 @@ struct hb_bit_set_t } public: - template - void process (const Op& op, const hb_bit_set_t &other) + void process_ (hb_bit_page_t::vector_t (*op) (const hb_bit_page_t::vector_t &, const hb_bit_page_t::vector_t &), + bool passthru_left, bool passthru_right, + const hb_bit_set_t &other) { - const bool passthru_left = op (1, 0); - const bool passthru_right = op (0, 1); - if (unlikely (!successful)) return; dirty (); @@ -542,21 +542,21 @@ struct hb_bit_set_t b = nb; for (; a && b; ) { - if (page_map[a - 1].major == other.page_map[b - 1].major) + if (page_map.arrayZ[a - 1].major == other.page_map.arrayZ[b - 1].major) { a--; b--; count--; - page_map[count] = page_map[a]; + page_map.arrayZ[count] = page_map.arrayZ[a]; page_at (count).v = op (page_at (a).v, other.page_at (b).v); } - else if (page_map[a - 1].major > other.page_map[b - 1].major) + else if (page_map.arrayZ[a - 1].major > other.page_map.arrayZ[b - 1].major) { a--; if (passthru_left) { count--; - page_map[count] = page_map[a]; + page_map.arrayZ[count] = page_map.arrayZ[a]; } } else @@ -565,8 +565,8 @@ struct hb_bit_set_t if (passthru_right) { count--; - page_map[count].major = other.page_map[b].major; - page_map[count].index = next_page++; + page_map.arrayZ[count].major = other.page_map.arrayZ[b].major; + page_map.arrayZ[count].index = next_page++; page_at (count).v = other.page_at (b).v; } } @@ -576,20 +576,29 @@ struct hb_bit_set_t { a--; count--; - page_map[count] = page_map [a]; + page_map.arrayZ[count] = page_map.arrayZ[a]; } if (passthru_right) while (b) { b--; count--; - page_map[count].major = other.page_map[b].major; - page_map[count].index = next_page++; + page_map.arrayZ[count].major = other.page_map.arrayZ[b].major; + page_map.arrayZ[count].index = next_page++; page_at (count).v = other.page_at (b).v; } assert (!count); resize (newCount); } + template + static hb_bit_page_t::vector_t + op_ (const hb_bit_page_t::vector_t &a, const hb_bit_page_t::vector_t &b) + { return Op{} (a, b); } + template + void process (const Op& op, const hb_bit_set_t &other) + { + process_ (op_, op (1, 0), op (0, 1), other); + } void union_ (const hb_bit_set_t &other) { process (hb_bitwise_or, other); } void intersect (const hb_bit_set_t &other) { process (hb_bitwise_and, other); } @@ -598,8 +607,6 @@ struct hb_bit_set_t bool next (hb_codepoint_t *codepoint) const { - // TODO: this should be merged with prev() as both implementations - // are very similar. if (unlikely (*codepoint == INVALID)) { *codepoint = get_min (); return *codepoint != INVALID; @@ -607,7 +614,7 @@ struct hb_bit_set_t const auto* page_map_array = page_map.arrayZ; unsigned int major = get_major (*codepoint); - unsigned int i = last_page_lookup.get_relaxed (); + unsigned int i = last_page_lookup; if (unlikely (i >= page_map.length || page_map_array[i].major != major)) { @@ -625,7 +632,7 @@ struct hb_bit_set_t if (pages_array[current.index].next (codepoint)) { *codepoint += current.major * page_t::PAGE_BITS; - last_page_lookup.set_relaxed (i); + last_page_lookup = i; return true; } i++; @@ -633,16 +640,16 @@ struct hb_bit_set_t for (; i < page_map.length; i++) { - const page_map_t ¤t = page_map.arrayZ[i]; + const page_map_t ¤t = page_map_array[i]; hb_codepoint_t m = pages_array[current.index].get_min (); if (m != INVALID) { *codepoint = current.major * page_t::PAGE_BITS + m; - last_page_lookup.set_relaxed (i); + last_page_lookup = i; return true; } } - last_page_lookup.set_relaxed (0); + last_page_lookup = 0; *codepoint = INVALID; return false; } @@ -656,21 +663,21 @@ struct hb_bit_set_t page_map_t map = {get_major (*codepoint), 0}; unsigned int i; page_map.bfind (map, &i, HB_NOT_FOUND_STORE_CLOSEST); - if (i < page_map.length && page_map[i].major == map.major) + if (i < page_map.length && page_map.arrayZ[i].major == map.major) { - if (pages[page_map[i].index].previous (codepoint)) + if (pages[page_map.arrayZ[i].index].previous (codepoint)) { - *codepoint += page_map[i].major * page_t::PAGE_BITS; + *codepoint += page_map.arrayZ[i].major * page_t::PAGE_BITS; return true; } } i--; for (; (int) i >= 0; i--) { - hb_codepoint_t m = pages[page_map[i].index].get_max (); + hb_codepoint_t m = pages.arrayZ[page_map.arrayZ[i].index].get_max (); if (m != INVALID) { - *codepoint = page_map[i].major * page_t::PAGE_BITS + m; + *codepoint = page_map.arrayZ[i].major * page_t::PAGE_BITS + m; return true; } } @@ -725,7 +732,7 @@ struct hb_bit_set_t { const auto* page_map_array = page_map.arrayZ; unsigned int major = get_major (codepoint); - unsigned int i = last_page_lookup.get_relaxed (); + unsigned int i = last_page_lookup; if (unlikely (i >= page_map.length || page_map_array[i].major != major)) { page_map.bfind (major, &i, HB_NOT_FOUND_STORE_CLOSEST); @@ -766,7 +773,7 @@ struct hb_bit_set_t { const auto* page_map_array = page_map.arrayZ; unsigned int major = get_major (codepoint); - unsigned int i = last_page_lookup.get_relaxed (); + unsigned int i = last_page_lookup; if (unlikely (i >= page_map.length || page_map_array[i].major != major)) { page_map.bfind(major, &i, HB_NOT_FOUND_STORE_CLOSEST); @@ -893,12 +900,12 @@ struct hb_bit_set_t /* The extra page_map length is necessary; can't just rely on vector here, * since the next check would be tricked because a null page also has * major==0, which we can't distinguish from an actualy major==0 page... */ - unsigned i = last_page_lookup.get_relaxed (); + unsigned i = last_page_lookup; if (likely (i < page_map.length)) { auto &cached_page = page_map.arrayZ[i]; if (cached_page.major == major) - return &pages[cached_page.index]; + return &pages.arrayZ[cached_page.index]; } page_map_t map = {major, pages.length}; @@ -910,15 +917,15 @@ struct hb_bit_set_t if (unlikely (!resize (pages.length + 1))) return nullptr; - pages[map.index].init0 (); - memmove (page_map + i + 1, - page_map + i, + pages.arrayZ[map.index].init0 (); + memmove (page_map.arrayZ + i + 1, + page_map.arrayZ + i, (page_map.length - 1 - i) * page_map.item_size); page_map[i] = map; } - last_page_lookup.set_relaxed (i); - return &pages[page_map[i].index]; + last_page_lookup = i; + return &pages.arrayZ[page_map.arrayZ[i].index]; } const page_t *page_for (hb_codepoint_t g) const { @@ -927,23 +934,31 @@ struct hb_bit_set_t /* The extra page_map length is necessary; can't just rely on vector here, * since the next check would be tricked because a null page also has * major==0, which we can't distinguish from an actualy major==0 page... */ - unsigned i = last_page_lookup.get_relaxed (); + unsigned i = last_page_lookup; if (likely (i < page_map.length)) { auto &cached_page = page_map.arrayZ[i]; if (cached_page.major == major) - return &pages[cached_page.index]; + return &pages.arrayZ[cached_page.index]; } page_map_t key = {major}; if (!page_map.bfind (key, &i)) return nullptr; - last_page_lookup.set_relaxed (i); - return &pages[page_map[i].index]; + last_page_lookup = i; + return &pages.arrayZ[page_map[i].index]; + } + page_t &page_at (unsigned int i) + { + assert (i < page_map.length); + return pages.arrayZ[page_map.arrayZ[i].index]; + } + const page_t &page_at (unsigned int i) const + { + assert (i < page_map.length); + return pages.arrayZ[page_map.arrayZ[i].index]; } - page_t &page_at (unsigned int i) { return pages[page_map[i].index]; } - const page_t &page_at (unsigned int i) const { return pages[page_map[i].index]; } unsigned int get_major (hb_codepoint_t g) const { return g >> page_t::PAGE_BITS_LOG_2; } unsigned int page_remainder (hb_codepoint_t g) const { return g & page_t::PAGE_BITMASK; } hb_codepoint_t major_start (unsigned int major) const { return major << page_t::PAGE_BITS_LOG_2; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-blob.cc b/src/java.desktop/share/native/libharfbuzz/hb-blob.cc index 1ee4195db7f..2a43afa1a43 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-blob.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-blob.cc @@ -99,7 +99,7 @@ hb_blob_create (const char *data, * is zero. This is in contrast to hb_blob_create(), which returns the singleton * empty blob (as returned by hb_blob_get_empty()) if @length is zero. * - * Return value: New blob, or %NULL if failed. Destroy with hb_blob_destroy(). + * Return value: New blob, or `NULL` if failed. Destroy with hb_blob_destroy(). * * Since: 2.8.2 **/ @@ -263,8 +263,6 @@ hb_blob_destroy (hb_blob_t *blob) { if (!hb_object_destroy (blob)) return; - blob->fini_shallow (); - hb_free (blob); } @@ -278,7 +276,7 @@ hb_blob_destroy (hb_blob_t *blob) * * Attaches a user-data key/data pair to the specified blob. * - * Return value: %true if success, %false otherwise + * Return value: `true` if success, `false` otherwise * * Since: 0.9.2 **/ @@ -305,7 +303,7 @@ hb_blob_set_user_data (hb_blob_t *blob, * Since: 0.9.2 **/ void * -hb_blob_get_user_data (hb_blob_t *blob, +hb_blob_get_user_data (const hb_blob_t *blob, hb_user_data_key_t *key) { return hb_object_get_user_data (blob, key); @@ -335,7 +333,7 @@ hb_blob_make_immutable (hb_blob_t *blob) * * Tests whether a blob is immutable. * - * Return value: %true if @blob is immutable, %false otherwise + * Return value: `true` if @blob is immutable, `false` otherwise * * Since: 0.9.2 **/ @@ -394,7 +392,7 @@ hb_blob_get_data (hb_blob_t *blob, unsigned int *length) * fails. * * Returns: (transfer none) (array length=length): Writable blob data, - * or %NULL if failed. + * or `NULL` if failed. * * Since: 0.9.2 **/ @@ -497,7 +495,7 @@ hb_blob_t::try_make_writable () DEBUG_MSG_FUNC (BLOB, this, "dupped successfully -> %p\n", this->data); - memcpy (new_data, this->data, this->length); + hb_memcpy (new_data, this->data, this->length); this->destroy_user_data (); this->mode = HB_MEMORY_MODE_WRITABLE; this->data = new_data; @@ -620,7 +618,7 @@ hb_blob_create_from_file (const char *file_name) * specified binary font file. * * Returns: An #hb_blob_t pointer with the content of the file, - * or %NULL if failed. + * or `NULL` if failed. * * Since: 2.8.2 **/ @@ -678,7 +676,7 @@ hb_blob_create_from_file_or_fail (const char *file_name) wchar_t * wchar_file_name = (wchar_t *) hb_malloc (sizeof (wchar_t) * size); if (unlikely (!wchar_file_name)) goto fail_without_close; mbstowcs (wchar_file_name, file_name, size); -#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) { CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 }; ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); @@ -699,7 +697,7 @@ hb_blob_create_from_file_or_fail (const char *file_name) if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close; -#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) { LARGE_INTEGER length; GetFileSizeEx (fd, &length); @@ -712,7 +710,7 @@ hb_blob_create_from_file_or_fail (const char *file_name) #endif if (unlikely (!file->mapping)) goto fail; -#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0); #else file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-blob.h b/src/java.desktop/share/native/libharfbuzz/hb-blob.h index 860b09bd088..b737665d93f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-blob.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-blob.h @@ -135,7 +135,7 @@ hb_blob_set_user_data (hb_blob_t *blob, HB_EXTERN void * -hb_blob_get_user_data (hb_blob_t *blob, +hb_blob_get_user_data (const hb_blob_t *blob, hb_user_data_key_t *key); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-blob.hh b/src/java.desktop/share/native/libharfbuzz/hb-blob.hh index a3683a681e7..b1b3b94d3dd 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-blob.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-blob.hh @@ -38,7 +38,7 @@ struct hb_blob_t { - void fini_shallow () { destroy_user_data (); } + ~hb_blob_t () { destroy_user_data (); } void destroy_user_data () { @@ -61,12 +61,12 @@ struct hb_blob_t public: hb_object_header_t header; - const char *data; - unsigned int length; - hb_memory_mode_t mode; + const char *data = nullptr; + unsigned int length = 0; + hb_memory_mode_t mode = (hb_memory_mode_t) 0; - void *user_data; - hb_destroy_func_t destroy; + void *user_data = nullptr; + hb_destroy_func_t destroy = nullptr; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-json.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-json.hh index 970fae7dfa6..7b9fc557f73 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-json.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-json.hh @@ -32,35 +32,38 @@ #include "hb.hh" -#line 36 "hb-buffer-deserialize-json.hh" +#line 33 "hb-buffer-deserialize-json.hh" static const unsigned char _deserialize_json_trans_keys[] = { 0u, 0u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, - 48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, - 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, - 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, - 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, - 34u, 92u, 9u, 125u, 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, - 9u, 125u, 9u, 93u, 9u, 123u, 0u, 0u, 0 + 48u, 57u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, + 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, + 9u, 125u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, + 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, + 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u, + 9u, 125u, 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, + 9u, 123u, 0u, 0u, 0 }; static const char _deserialize_json_key_spans[] = { 0, 115, 26, 21, 2, 1, 50, 49, - 10, 117, 117, 117, 1, 50, 49, 10, - 117, 117, 1, 1, 50, 49, 117, 117, - 2, 1, 50, 49, 10, 117, 117, 1, - 50, 49, 10, 117, 117, 1, 50, 49, - 59, 117, 59, 117, 117, 1, 50, 49, - 117, 85, 115, 0 + 10, 117, 117, 85, 117, 1, 50, 49, + 10, 117, 117, 1, 1, 50, 49, 117, + 117, 2, 1, 50, 49, 10, 117, 117, + 1, 50, 49, 10, 117, 117, 1, 1, + 50, 49, 117, 117, 1, 50, 49, 59, + 117, 59, 117, 117, 1, 50, 49, 117, + 115, 0 }; static const short _deserialize_json_index_offsets[] = { 0, 0, 116, 143, 165, 168, 170, 221, - 271, 282, 400, 518, 636, 638, 689, 739, - 750, 868, 986, 988, 990, 1041, 1091, 1209, - 1327, 1330, 1332, 1383, 1433, 1444, 1562, 1680, - 1682, 1733, 1783, 1794, 1912, 2030, 2032, 2083, - 2133, 2193, 2311, 2371, 2489, 2607, 2609, 2660, - 2710, 2828, 2914, 3030 + 271, 282, 400, 518, 604, 722, 724, 775, + 825, 836, 954, 1072, 1074, 1076, 1127, 1177, + 1295, 1413, 1416, 1418, 1469, 1519, 1530, 1648, + 1766, 1768, 1819, 1869, 1880, 1998, 2116, 2118, + 2120, 2171, 2221, 2339, 2457, 2459, 2510, 2560, + 2620, 2738, 2798, 2916, 3034, 3036, 3087, 3137, + 3255, 3371 }; static const char _deserialize_json_indicies[] = { @@ -82,28 +85,28 @@ static const char _deserialize_json_indicies[] = { 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 4, 1, - 5, 1, 6, 7, 1, 1, 8, 1, + 5, 1, 6, 7, 1, 8, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 9, 1, 10, 11, - 1, 12, 1, 12, 12, 12, 12, 12, + 1, 1, 1, 1, 10, 1, 11, 12, + 1, 13, 1, 13, 13, 13, 13, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 12, 1, 1, 1, 1, 1, + 1, 1, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 13, 1, 13, 13, - 13, 13, 13, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 14, 1, 14, 14, + 14, 14, 14, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 13, 1, 1, + 1, 1, 1, 1, 1, 14, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 14, 1, 1, 15, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 1, - 17, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 1, 19, 19, 19, 19, 19, + 1, 1, 15, 1, 1, 16, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 1, + 18, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 1, 20, 20, 20, 20, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 19, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 20, 1, + 1, 1, 20, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 21, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -113,11 +116,11 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 21, - 1, 22, 22, 22, 22, 22, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 22, + 1, 23, 23, 23, 23, 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 22, 1, 1, 1, 1, 1, 1, 1, + 23, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -128,85 +131,94 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 23, 1, 19, - 19, 19, 19, 19, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 24, 1, 25, + 25, 25, 25, 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 19, 1, + 1, 1, 1, 1, 1, 1, 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 20, 1, 1, 1, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 18, + 1, 1, 26, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 27, 1, 20, 20, 20, + 20, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 21, 1, 1, 1, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 21, 1, 24, 1, 24, - 24, 24, 24, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 25, 1, 25, 25, 25, 25, 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 25, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 26, 1, - 1, 27, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 1, 29, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 1, 31, - 31, 31, 31, 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 31, 1, + 1, 22, 1, 28, 1, 28, 28, 28, + 28, 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 32, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 29, 1, + 29, 29, 29, 29, 29, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 29, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 30, 1, 1, 31, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 1, 33, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 1, 35, 35, 35, + 35, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 36, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 33, 1, 31, 31, 31, - 31, 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 32, 1, 1, 1, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 37, 1, 35, 35, 35, 35, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 35, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 36, 1, + 1, 1, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 33, 1, 34, 1, 35, 1, 35, - 35, 35, 35, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 36, 1, 36, 36, 36, 36, 36, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 37, + 1, 38, 1, 39, 1, 39, 39, 39, + 39, 39, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 36, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 39, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 37, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 1, 39, 39, 39, 39, - 39, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 39, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 40, 1, + 40, 40, 40, 40, 40, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 40, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 41, + 42, 42, 42, 42, 42, 42, 42, 42, + 42, 1, 43, 43, 43, 43, 43, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 43, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 44, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -215,43 +227,42 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 41, 1, 39, 39, 39, 39, 39, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 45, 1, + 43, 43, 43, 43, 43, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 39, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 40, 1, 1, - 1, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 43, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 44, 1, 1, 1, 46, + 46, 46, 46, 46, 46, 46, 46, 46, + 46, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 41, 1, - 43, 44, 1, 45, 1, 45, 45, 45, - 45, 45, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 45, 1, 1, 1, + 1, 1, 1, 1, 45, 1, 47, 48, + 1, 49, 1, 49, 49, 49, 49, 49, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 46, 1, - 46, 46, 46, 46, 46, 1, 1, 1, + 1, 1, 49, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 46, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 47, 1, 1, 48, - 49, 49, 49, 49, 49, 49, 49, 49, - 49, 1, 50, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 1, 52, 52, 52, - 52, 52, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 50, 1, 50, 50, + 50, 50, 50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 52, 1, 1, 1, + 1, 1, 1, 1, 1, 50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 53, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 51, 1, 1, 52, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 1, + 54, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 1, 56, 56, 56, 56, 56, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 56, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 57, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -259,42 +270,43 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 54, 1, 52, 52, 52, 52, 52, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 52, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 53, 1, - 1, 1, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 58, + 1, 56, 56, 56, 56, 56, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 56, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 57, 1, 1, 1, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 54, - 1, 55, 1, 55, 55, 55, 55, 55, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 55, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 58, 1, 59, + 1, 59, 59, 59, 59, 59, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 56, 1, 56, 56, - 56, 56, 56, 1, 1, 1, 1, 1, + 59, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 56, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 57, 1, 1, 58, 59, 59, - 59, 59, 59, 59, 59, 59, 59, 1, - 60, 61, 61, 61, 61, 61, 61, 61, - 61, 61, 1, 62, 62, 62, 62, 62, + 1, 1, 60, 1, 60, 60, 60, 60, + 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 62, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 63, 1, + 61, 1, 1, 62, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 1, 64, 65, + 65, 65, 65, 65, 65, 65, 65, 65, + 1, 66, 66, 66, 66, 66, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 66, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 67, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -302,48 +314,42 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 64, - 1, 62, 62, 62, 62, 62, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 62, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 63, 1, 1, 1, - 61, 61, 61, 61, 61, 61, 61, 61, - 61, 61, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 68, 1, 66, + 66, 66, 66, 66, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 66, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 67, 1, 1, 1, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 64, 1, 65, - 1, 65, 65, 65, 65, 65, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 65, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 68, 1, 69, 1, 70, + 1, 70, 70, 70, 70, 70, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 66, 1, 66, 66, 66, 66, - 66, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 66, 1, 67, 1, 1, + 1, 1, 71, 1, 71, 71, 71, 71, + 71, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 68, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 1, 71, 70, - 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, - 72, 70, 73, 73, 73, 73, 73, 1, + 1, 1, 1, 71, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 72, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 1, 74, 74, + 74, 74, 74, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 73, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 74, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -352,86 +358,126 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 75, 1, - 70, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 76, 1, 74, 74, 74, 74, + 74, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 74, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 75, + 1, 1, 1, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 76, 1, 78, 1, 78, 78, 78, 78, + 78, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 78, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 79, 1, 79, + 79, 79, 79, 79, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 79, 1, + 80, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 81, 82, + 82, 82, 82, 82, 82, 82, 82, 82, + 1, 84, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 85, 83, 86, 86, 86, + 86, 86, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 86, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 87, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 70, 1, 76, 76, 76, 76, - 76, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 76, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 77, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 88, 1, 83, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 83, 1, 89, + 89, 89, 89, 89, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 89, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 78, 1, 76, 76, 76, 76, 76, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 76, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 77, 1, 1, - 1, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 91, 1, 89, 89, 89, + 89, 89, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 78, 1, - 80, 1, 80, 80, 80, 80, 80, 1, + 1, 1, 1, 1, 89, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 90, 1, 1, 1, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 81, 1, 81, 81, 81, - 81, 81, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 81, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 82, 83, 83, 83, - 83, 83, 83, 83, 83, 83, 1, 76, - 76, 76, 76, 76, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 76, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 77, 1, 1, 1, 84, 84, - 84, 84, 84, 84, 84, 84, 84, 84, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 91, 1, 93, 1, 93, 93, 93, + 93, 93, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 93, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 94, 1, + 94, 94, 94, 94, 94, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 94, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 95, + 96, 96, 96, 96, 96, 96, 96, 96, + 96, 1, 89, 89, 89, 89, 89, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 78, 1, 85, 85, 85, - 85, 85, 1, 1, 1, 1, 1, 1, + 1, 89, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 90, 1, 1, + 1, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 85, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 86, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 87, 1, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 91, 1, + 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -442,46 +488,49 @@ static const char _deserialize_json_indicies[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 2, 1, 1, - 0 + 1, 1, 2, 1, 1, 0 }; static const char _deserialize_json_trans_targs[] = { - 1, 0, 2, 2, 3, 4, 18, 24, - 37, 45, 5, 12, 6, 7, 8, 9, - 11, 9, 11, 10, 2, 49, 10, 49, - 13, 14, 15, 16, 17, 16, 17, 10, - 2, 49, 19, 20, 21, 22, 23, 10, - 2, 49, 23, 25, 31, 26, 27, 28, - 29, 30, 29, 30, 10, 2, 49, 32, - 33, 34, 35, 36, 35, 36, 10, 2, - 49, 38, 39, 40, 43, 44, 40, 41, - 42, 10, 2, 49, 10, 2, 49, 44, - 46, 47, 43, 48, 48, 49, 50, 51 + 1, 0, 2, 2, 3, 4, 19, 25, + 38, 44, 52, 5, 13, 6, 7, 8, + 9, 12, 9, 12, 10, 2, 11, 10, + 11, 11, 56, 57, 14, 15, 16, 17, + 18, 17, 18, 10, 2, 11, 20, 21, + 22, 23, 24, 10, 2, 11, 24, 26, + 32, 27, 28, 29, 30, 31, 30, 31, + 10, 2, 11, 33, 34, 35, 36, 37, + 36, 37, 10, 2, 11, 39, 40, 41, + 42, 43, 10, 2, 11, 43, 45, 46, + 47, 50, 51, 47, 48, 49, 10, 2, + 11, 10, 2, 11, 51, 53, 54, 50, + 55, 55 }; static const char _deserialize_json_trans_actions[] = { 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 2, - 2, 0, 0, 3, 3, 4, 0, 5, - 0, 0, 2, 2, 2, 0, 0, 6, - 6, 7, 0, 0, 0, 2, 2, 8, - 8, 9, 0, 0, 0, 0, 0, 2, - 2, 2, 0, 0, 10, 10, 11, 0, - 0, 2, 2, 2, 0, 0, 12, 12, - 13, 0, 0, 2, 14, 14, 0, 15, - 0, 16, 16, 17, 18, 18, 19, 15, - 0, 0, 20, 20, 21, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 2, + 2, 2, 0, 0, 3, 3, 4, 0, + 5, 0, 0, 0, 0, 0, 2, 2, + 2, 0, 0, 6, 6, 7, 0, 0, + 0, 2, 2, 8, 8, 9, 0, 0, + 0, 0, 0, 2, 2, 2, 0, 0, + 10, 10, 11, 0, 0, 2, 2, 2, + 0, 0, 12, 12, 13, 0, 0, 0, + 2, 2, 14, 14, 15, 0, 0, 0, + 2, 16, 16, 0, 17, 0, 18, 18, + 19, 20, 20, 21, 17, 0, 0, 22, + 22, 23 }; static const int deserialize_json_start = 1; -static const int deserialize_json_first_final = 49; +static const int deserialize_json_first_final = 56; static const int deserialize_json_error = 0; static const int deserialize_json_en_main = 1; -#line 108 "hb-buffer-deserialize-json.rl" +#line 111 "hb-buffer-deserialize-json.rl" static hb_bool_t @@ -499,21 +548,19 @@ _hb_buffer_deserialize_json (hb_buffer_t *buffer, while (p < pe && ISSPACE (*p)) p++; if (p < pe && *p == (buffer->len ? ',' : '[')) - { *end_ptr = ++p; - } const char *tok = nullptr; int cs; hb_glyph_info_t info = {0}; hb_glyph_position_t pos = {0}; -#line 512 "hb-buffer-deserialize-json.hh" +#line 552 "hb-buffer-deserialize-json.hh" { cs = deserialize_json_start; } -#line 517 "hb-buffer-deserialize-json.hh" +#line 555 "hb-buffer-deserialize-json.hh" { int _slen; int _trans; @@ -541,8 +588,8 @@ _resume: case 1: #line 38 "hb-buffer-deserialize-json.rl" { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); } break; case 5: @@ -561,25 +608,25 @@ _resume: tok = p; } break; - case 15: + case 17: #line 55 "hb-buffer-deserialize-json.rl" { if (unlikely (!buffer->ensure_glyphs ())) return false; } break; - case 21: + case 23: #line 56 "hb-buffer-deserialize-json.rl" { if (unlikely (!buffer->ensure_unicode ())) return false; } break; - case 16: + case 18: #line 58 "hb-buffer-deserialize-json.rl" { /* TODO Unescape \" and \\ if found. */ if (!hb_font_glyph_from_string (font, - tok, p - tok, + tok+1, p - tok - 2, /* Skip "" */ &info.codepoint)) return false; } break; - case 18: + case 20: #line 66 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.codepoint)) return false; } break; @@ -604,6 +651,10 @@ _resume: { if (!parse_int (tok, p, &pos.y_advance)) return false; } break; case 14: +#line 72 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } + break; + case 16: #line 51 "hb-buffer-deserialize-json.rl" { tok = p; @@ -611,7 +662,7 @@ _resume: #line 55 "hb-buffer-deserialize-json.rl" { if (unlikely (!buffer->ensure_glyphs ())) return false; } break; - case 20: + case 22: #line 51 "hb-buffer-deserialize-json.rl" { tok = p; @@ -619,12 +670,12 @@ _resume: #line 56 "hb-buffer-deserialize-json.rl" { if (unlikely (!buffer->ensure_unicode ())) return false; } break; - case 17: + case 19: #line 58 "hb-buffer-deserialize-json.rl" { /* TODO Unescape \" and \\ if found. */ if (!hb_font_glyph_from_string (font, - tok, p - tok, + tok+1, p - tok - 2, /* Skip "" */ &info.codepoint)) return false; } @@ -637,7 +688,7 @@ _resume: *end_ptr = p; } break; - case 19: + case 21: #line 66 "hb-buffer-deserialize-json.rl" { if (!parse_uint (tok, p, &info.codepoint)) return false; } #line 43 "hb-buffer-deserialize-json.rl" @@ -709,7 +760,19 @@ _resume: *end_ptr = p; } break; -#line 713 "hb-buffer-deserialize-json.hh" + case 15: +#line 72 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 733 "hb-buffer-deserialize-json.hh" } _again: @@ -721,7 +784,7 @@ _again: _out: {} } -#line 136 "hb-buffer-deserialize-json.rl" +#line 137 "hb-buffer-deserialize-json.rl" *end_ptr = p; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-glyphs.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-glyphs.hh new file mode 100644 index 00000000000..cf9c281e86e --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-glyphs.hh @@ -0,0 +1,692 @@ + +#line 1 "hb-buffer-deserialize-text-glyphs.rl" +/* + * Copyright © 2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_DESERIALIZE_TEXT_GLYPHS_HH +#define HB_BUFFER_DESERIALIZE_TEXT_GLYPHS_HH + +#include "hb.hh" + + +#line 33 "hb-buffer-deserialize-text-glyphs.hh" +static const unsigned char _deserialize_text_glyphs_trans_keys[] = { + 0u, 0u, 48u, 57u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, + 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 43u, 124u, 9u, 124u, 9u, 124u, + 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, + 9u, 124u, 9u, 124u, 9u, 124u, 0 +}; + +static const char _deserialize_text_glyphs_key_spans[] = { + 0, 10, 13, 10, 13, 10, 10, 13, + 10, 1, 13, 10, 14, 82, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116 +}; + +static const short _deserialize_text_glyphs_index_offsets[] = { + 0, 0, 11, 25, 36, 50, 61, 72, + 86, 97, 99, 113, 124, 139, 222, 339, + 456, 573, 690, 807, 924, 1041, 1158, 1275, + 1392, 1509, 1626 +}; + +static const char _deserialize_text_glyphs_indicies[] = { + 0, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 1, 3, 1, 1, 4, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 1, 6, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 1, 8, 1, 1, + 9, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 1, 11, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 1, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 1, 15, 1, 1, 16, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 1, 18, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 1, 20, 1, 21, 1, 1, 22, + 23, 23, 23, 23, 23, 23, 23, 23, + 23, 1, 24, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 1, 20, 1, 1, + 1, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 1, 26, 26, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 26, 1, + 1, 26, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 26, 26, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 26, 1, 28, + 28, 28, 28, 28, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 28, 27, + 27, 29, 27, 27, 27, 27, 27, 27, + 27, 30, 1, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 31, 27, 27, 32, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 33, 1, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 28, 27, 34, 34, 34, 34, + 34, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 34, 26, 26, 35, 26, + 26, 26, 26, 26, 26, 26, 36, 1, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 37, 26, 26, 38, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 39, + 1, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 40, + 26, 41, 41, 41, 41, 41, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 41, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 42, 1, 43, 43, + 43, 43, 43, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 43, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 44, 1, 41, 41, 41, 41, 41, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 41, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 42, 1, + 46, 46, 46, 46, 46, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 46, + 1, 1, 47, 1, 1, 1, 1, 1, + 1, 1, 1, 48, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 49, 1, 50, 50, 50, + 50, 50, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 50, 1, 1, 51, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 52, 1, 50, 50, 50, 50, 50, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 50, 1, 1, 51, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 52, 1, 46, + 46, 46, 46, 46, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 46, 1, + 1, 47, 1, 1, 1, 1, 1, 1, + 1, 1, 48, 1, 1, 1, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 49, 1, 53, 53, 53, 53, + 53, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 53, 1, 1, 54, 1, + 1, 1, 1, 1, 1, 1, 55, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 56, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 57, + 1, 58, 58, 58, 58, 58, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 58, 1, 1, 59, 1, 1, 1, 1, + 1, 1, 1, 60, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 61, 1, 58, 58, + 58, 58, 58, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 58, 1, 1, + 59, 1, 1, 1, 1, 1, 1, 1, + 60, 1, 1, 1, 1, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 61, 1, 53, 53, 53, 53, 53, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 53, 1, 1, 54, 1, 1, + 1, 1, 1, 1, 1, 55, 1, 1, + 1, 1, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 1, 1, 1, 1, + 1, 1, 56, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 57, 1, + 0 +}; + +static const char _deserialize_text_glyphs_trans_targs[] = { + 16, 0, 18, 3, 19, 22, 19, 22, + 5, 20, 21, 20, 21, 23, 26, 8, + 9, 12, 9, 12, 10, 11, 24, 25, + 24, 25, 15, 15, 14, 1, 2, 6, + 7, 13, 15, 1, 2, 6, 7, 13, + 14, 17, 14, 17, 14, 18, 17, 1, + 4, 14, 17, 1, 14, 17, 1, 2, + 7, 14, 17, 1, 2, 14, 26 +}; + +static const char _deserialize_text_glyphs_trans_actions[] = { + 1, 0, 1, 1, 1, 1, 0, 0, + 1, 1, 1, 0, 0, 1, 1, 1, + 1, 1, 0, 0, 2, 1, 1, 1, + 0, 0, 0, 4, 3, 5, 5, 5, + 5, 4, 6, 7, 7, 7, 7, 0, + 6, 8, 8, 0, 0, 0, 9, 10, + 10, 9, 11, 12, 11, 13, 14, 14, + 14, 13, 15, 16, 16, 15, 0 +}; + +static const char _deserialize_text_glyphs_eof_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3, 6, + 8, 0, 8, 9, 11, 11, 9, 13, + 15, 15, 13 +}; + +static const int deserialize_text_glyphs_start = 14; +static const int deserialize_text_glyphs_first_final = 14; +static const int deserialize_text_glyphs_error = 0; + +static const int deserialize_text_glyphs_en_main = 14; + + +#line 98 "hb-buffer-deserialize-text-glyphs.rl" + + +static hb_bool_t +_hb_buffer_deserialize_text_glyphs (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len, *eof = pe, *orig_pe = pe; + + /* Ensure we have positions. */ + (void) hb_buffer_get_glyph_positions (buffer, nullptr); + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? '|' : '[')) + *end_ptr = ++p; + + const char *end = strchr ((char *) p, ']'); + if (end) + pe = eof = end; + else + { + end = strrchr ((char *) p, '|'); + if (end) + pe = eof = end; + else + pe = eof = p; + } + + const char *tok = nullptr; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + +#line 346 "hb-buffer-deserialize-text-glyphs.hh" + { + cs = deserialize_text_glyphs_start; + } + +#line 349 "hb-buffer-deserialize-text-glyphs.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _deserialize_text_glyphs_trans_keys + (cs<<1); + _inds = _deserialize_text_glyphs_indicies + _deserialize_text_glyphs_index_offsets[cs]; + + _slen = _deserialize_text_glyphs_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _deserialize_text_glyphs_trans_targs[_trans]; + + if ( _deserialize_text_glyphs_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _deserialize_text_glyphs_trans_actions[_trans] ) { + case 1: +#line 51 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} + break; + case 7: +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 14: +#line 63 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 2: +#line 64 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } + break; + case 16: +#line 65 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 10: +#line 66 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 12: +#line 67 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } + break; + case 4: +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} + break; + case 6: +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 13: +#line 63 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 15: +#line 65 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 66 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 67 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 8: +#line 68 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 5: +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 3: +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 516 "hb-buffer-deserialize-text-glyphs.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + switch ( _deserialize_text_glyphs_eof_actions[cs] ) { + case 6: +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 13: +#line 63 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 15: +#line 65 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 66 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 67 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 8: +#line 68 "hb-buffer-deserialize-text-glyphs.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 3: +#line 38 "hb-buffer-deserialize-text-glyphs.rl" + { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text-glyphs.rl" + { + tok = p; +} +#line 55 "hb-buffer-deserialize-text-glyphs.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text-glyphs.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 616 "hb-buffer-deserialize-text-glyphs.hh" + } + } + + _out: {} + } + +#line 136 "hb-buffer-deserialize-text-glyphs.rl" + + + if (pe < orig_pe && *pe == ']') + { + pe++; + if (p == pe) + p++; + } + + *end_ptr = p; + + return p == pe; +} + +#endif /* HB_BUFFER_DESERIALIZE_TEXT_GLYPHS_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh new file mode 100644 index 00000000000..f0c94654533 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh @@ -0,0 +1,332 @@ + +#line 1 "hb-buffer-deserialize-text-unicode.rl" +/* + * Copyright © 2013 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_DESERIALIZE_TEXT_UNICODE_HH +#define HB_BUFFER_DESERIALIZE_TEXT_UNICODE_HH + +#include "hb.hh" + + +#line 33 "hb-buffer-deserialize-text-unicode.hh" +static const unsigned char _deserialize_text_unicode_trans_keys[] = { + 0u, 0u, 9u, 117u, 43u, 102u, 48u, 102u, 48u, 57u, 9u, 124u, 9u, 124u, 9u, 124u, + 9u, 124u, 0 +}; + +static const char _deserialize_text_unicode_key_spans[] = { + 0, 109, 60, 55, 10, 116, 116, 116, + 116 +}; + +static const short _deserialize_text_unicode_index_offsets[] = { + 0, 0, 110, 171, 227, 238, 355, 472, + 589 +}; + +static const char _deserialize_text_unicode_indicies[] = { + 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 3, + 1, 1, 1, 1, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 1, 1, + 1, 1, 1, 1, 1, 4, 4, 4, + 4, 4, 4, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4, 4, 4, + 4, 4, 4, 1, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 1, 1, + 1, 1, 1, 1, 1, 4, 4, 4, + 4, 4, 4, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4, 4, 4, + 4, 4, 4, 1, 5, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 1, 7, + 7, 7, 7, 7, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 7, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 1, 1, 1, 9, 1, 1, 1, 8, + 8, 8, 8, 8, 8, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8, + 8, 8, 8, 8, 8, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 10, 1, 11, 11, 11, 11, + 11, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 11, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, + 1, 12, 12, 12, 12, 12, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 12, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 13, 1, 12, 12, + 12, 12, 12, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 12, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 13, 1, 0 +}; + +static const char _deserialize_text_unicode_trans_targs[] = { + 1, 0, 2, 3, 5, 7, 8, 6, + 5, 4, 1, 6, 6, 1, 8 +}; + +static const char _deserialize_text_unicode_trans_actions[] = { + 0, 0, 1, 0, 2, 2, 2, 3, + 0, 4, 3, 0, 5, 5, 0 +}; + +static const char _deserialize_text_unicode_eof_actions[] = { + 0, 0, 0, 0, 0, 3, 0, 5, + 5 +}; + +static const int deserialize_text_unicode_start = 1; +static const int deserialize_text_unicode_first_final = 5; +static const int deserialize_text_unicode_error = 0; + +static const int deserialize_text_unicode_en_main = 1; + + +#line 79 "hb-buffer-deserialize-text-unicode.rl" + + +static hb_bool_t +_hb_buffer_deserialize_text_unicode (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len, *eof = pe, *orig_pe = pe; + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? '|' : '<')) + *end_ptr = ++p; + + const char *end = strchr ((char *) p, '>'); + if (end) + pe = eof = end; + else + { + end = strrchr ((char *) p, '|'); + if (end) + pe = eof = end; + else + pe = eof = p; + } + + + const char *tok = nullptr; + int cs; + hb_glyph_info_t info = {0}; + const hb_glyph_position_t pos = {0}; + +#line 194 "hb-buffer-deserialize-text-unicode.hh" + { + cs = deserialize_text_unicode_start; + } + +#line 197 "hb-buffer-deserialize-text-unicode.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _deserialize_text_unicode_trans_keys + (cs<<1); + _inds = _deserialize_text_unicode_indicies + _deserialize_text_unicode_index_offsets[cs]; + + _slen = _deserialize_text_unicode_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _deserialize_text_unicode_trans_targs[_trans]; + + if ( _deserialize_text_unicode_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _deserialize_text_unicode_trans_actions[_trans] ) { + case 1: +#line 38 "hb-buffer-deserialize-text-unicode.rl" + { + hb_memset (&info, 0, sizeof (info)); +} + break; + case 2: +#line 51 "hb-buffer-deserialize-text-unicode.rl" + { + tok = p; +} + break; + case 4: +#line 55 "hb-buffer-deserialize-text-unicode.rl" + {if (!parse_hex (tok, p, &info.codepoint )) return false; } + break; + case 3: +#line 55 "hb-buffer-deserialize-text-unicode.rl" + {if (!parse_hex (tok, p, &info.codepoint )) return false; } +#line 42 "hb-buffer-deserialize-text-unicode.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + if (buffer->have_positions) + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 5: +#line 57 "hb-buffer-deserialize-text-unicode.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 42 "hb-buffer-deserialize-text-unicode.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + if (buffer->have_positions) + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 256 "hb-buffer-deserialize-text-unicode.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + switch ( _deserialize_text_unicode_eof_actions[cs] ) { + case 3: +#line 55 "hb-buffer-deserialize-text-unicode.rl" + {if (!parse_hex (tok, p, &info.codepoint )) return false; } +#line 42 "hb-buffer-deserialize-text-unicode.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + if (buffer->have_positions) + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 5: +#line 57 "hb-buffer-deserialize-text-unicode.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 42 "hb-buffer-deserialize-text-unicode.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + if (buffer->have_positions) + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 289 "hb-buffer-deserialize-text-unicode.hh" + } + } + + _out: {} + } + +#line 115 "hb-buffer-deserialize-text-unicode.rl" + + + if (pe < orig_pe && *pe == '>') + { + pe++; + if (p == pe) + p++; + } + + *end_ptr = p; + + return p == pe; +} + +#endif /* HB_BUFFER_DESERIALIZE_TEXT_UNICODE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text.hh deleted file mode 100644 index d34d5a41a4a..00000000000 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text.hh +++ /dev/null @@ -1,853 +0,0 @@ - -#line 1 "hb-buffer-deserialize-text.rl" -/* - * Copyright © 2013 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_BUFFER_DESERIALIZE_TEXT_HH -#define HB_BUFFER_DESERIALIZE_TEXT_HH - -#include "hb.hh" - - -#line 36 "hb-buffer-deserialize-text.hh" -static const unsigned char _deserialize_text_trans_keys[] = { - 0u, 0u, 9u, 91u, 85u, 85u, 43u, 43u, 48u, 102u, 9u, 85u, 48u, 57u, 45u, 57u, - 48u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, - 43u, 124u, 45u, 57u, 48u, 57u, 9u, 124u, 9u, 124u, 0u, 0u, 9u, 85u, 9u, 124u, - 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, - 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 0 -}; - -static const char _deserialize_text_key_spans[] = { - 0, 83, 1, 1, 55, 77, 10, 13, - 10, 10, 13, 10, 1, 13, 10, 14, - 82, 13, 10, 116, 116, 0, 77, 116, - 116, 116, 116, 116, 116, 116, 116, 116, - 116, 116, 116, 116, 116 -}; - -static const short _deserialize_text_index_offsets[] = { - 0, 0, 84, 86, 88, 144, 222, 233, - 247, 258, 269, 283, 294, 296, 310, 321, - 336, 419, 433, 444, 561, 678, 679, 757, - 874, 991, 1108, 1225, 1342, 1459, 1576, 1693, - 1810, 1927, 2044, 2161, 2278 -}; - -static const char _deserialize_text_indicies[] = { - 0, 0, 0, 0, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 2, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 3, 1, 4, 1, 5, - 1, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 1, 1, 1, 1, 1, - 1, 1, 6, 6, 6, 6, 6, 6, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 6, 6, 6, 6, 6, 6, - 1, 7, 7, 7, 7, 7, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 7, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 4, 1, 8, - 9, 9, 9, 9, 9, 9, 9, 9, - 9, 1, 10, 1, 1, 11, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 1, - 13, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 1, 15, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 1, 17, 1, - 1, 18, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 1, 20, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 1, 22, - 1, 23, 1, 1, 24, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 1, 26, - 27, 27, 27, 27, 27, 27, 27, 27, - 27, 1, 22, 1, 1, 1, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 21, - 1, 28, 28, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 28, 1, 1, 28, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 28, 28, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 28, 1, 29, 1, 1, 30, - 31, 31, 31, 31, 31, 31, 31, 31, - 31, 1, 32, 33, 33, 33, 33, 33, - 33, 33, 33, 33, 1, 34, 34, 34, - 34, 34, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 34, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 1, 1, - 1, 36, 37, 1, 1, 35, 35, 35, - 35, 35, 35, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 35, 35, 35, - 35, 35, 35, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 38, 1, 39, 39, 39, 39, 39, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 39, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 40, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 41, 1, 1, - 7, 7, 7, 7, 7, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 7, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 4, 1, 42, 42, - 42, 42, 42, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 42, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 43, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 44, 1, 42, 42, 42, 42, 42, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 42, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 1, 1, 1, 1, - 43, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 44, 1, - 47, 47, 47, 47, 47, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 47, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 48, 1, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 49, 46, 46, 50, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 51, 52, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 53, 46, 54, 54, 54, - 54, 54, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 54, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 55, - 1, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, - 28, 56, 28, 28, 57, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, - 58, 59, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, - 60, 28, 61, 61, 61, 61, 61, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 61, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 62, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 63, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 64, 1, 65, - 65, 65, 65, 65, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 65, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 40, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 66, 1, 67, 67, 67, 67, - 67, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 67, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 48, 1, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 49, 46, 46, 50, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 51, - 52, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 53, - 46, 68, 68, 68, 68, 68, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 68, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 69, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 70, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 43, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 71, 1, 72, 72, - 72, 72, 72, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 72, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 73, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 74, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 75, 1, 72, 72, 72, 72, 72, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 72, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 73, 1, 1, - 1, 1, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 74, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 75, 1, - 68, 68, 68, 68, 68, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 68, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 69, 1, 1, 1, 1, 76, - 76, 76, 76, 76, 76, 76, 76, 76, - 76, 1, 1, 1, 1, 1, 1, 70, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 43, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 71, 1, 77, 77, 77, - 77, 77, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 77, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 78, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 79, 1, 77, 77, 77, 77, 77, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 77, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 33, 33, 33, 33, 33, 33, 33, - 33, 33, 33, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 78, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 79, 1, 61, - 61, 61, 61, 61, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 61, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 62, 1, 1, 1, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 63, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 64, 1, 0 -}; - -static const char _deserialize_text_trans_targs[] = { - 1, 0, 2, 25, 3, 4, 19, 5, - 23, 24, 8, 27, 36, 27, 36, 30, - 33, 11, 12, 15, 12, 15, 13, 14, - 31, 32, 31, 32, 26, 18, 34, 35, - 34, 35, 20, 19, 6, 21, 22, 20, - 21, 22, 20, 21, 22, 24, 26, 26, - 7, 9, 10, 16, 21, 29, 26, 7, - 9, 10, 16, 21, 29, 28, 17, 21, - 29, 28, 29, 29, 28, 7, 10, 29, - 28, 7, 21, 29, 33, 28, 21, 29 -}; - -static const char _deserialize_text_trans_actions[] = { - 0, 0, 0, 0, 1, 0, 2, 0, - 2, 2, 3, 4, 4, 5, 5, 4, - 4, 3, 3, 3, 0, 0, 6, 3, - 4, 4, 5, 5, 5, 3, 4, 4, - 5, 5, 7, 8, 9, 7, 7, 0, - 0, 0, 10, 10, 10, 8, 12, 13, - 14, 14, 14, 15, 11, 11, 17, 18, - 18, 18, 0, 16, 16, 19, 20, 19, - 19, 0, 0, 13, 10, 21, 21, 10, - 22, 23, 22, 22, 5, 24, 24, 24 -}; - -static const char _deserialize_text_eof_actions[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 7, 0, 0, 0, 10, - 10, 11, 16, 19, 0, 11, 10, 22, - 22, 10, 24, 24, 19 -}; - -static const int deserialize_text_start = 1; -static const int deserialize_text_first_final = 19; -static const int deserialize_text_error = 0; - -static const int deserialize_text_en_main = 1; - - -#line 114 "hb-buffer-deserialize-text.rl" - - -static hb_bool_t -_hb_buffer_deserialize_text (hb_buffer_t *buffer, - const char *buf, - unsigned int buf_len, - const char **end_ptr, - hb_font_t *font) -{ - const char *p = buf, *pe = buf + buf_len; - - /* Ensure we have positions. */ - (void) hb_buffer_get_glyph_positions (buffer, nullptr); - - while (p < pe && ISSPACE (*p)) - p++; - - const char *eof = pe, *tok = nullptr; - int cs; - hb_glyph_info_t info = {0}; - hb_glyph_position_t pos = {0}; - -#line 428 "hb-buffer-deserialize-text.hh" - { - cs = deserialize_text_start; - } - -#line 433 "hb-buffer-deserialize-text.hh" - { - int _slen; - int _trans; - const unsigned char *_keys; - const char *_inds; - if ( p == pe ) - goto _test_eof; - if ( cs == 0 ) - goto _out; -_resume: - _keys = _deserialize_text_trans_keys + (cs<<1); - _inds = _deserialize_text_indicies + _deserialize_text_index_offsets[cs]; - - _slen = _deserialize_text_key_spans[cs]; - _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && - (*p) <= _keys[1] ? - (*p) - _keys[0] : _slen ]; - - cs = _deserialize_text_trans_targs[_trans]; - - if ( _deserialize_text_trans_actions[_trans] == 0 ) - goto _again; - - switch ( _deserialize_text_trans_actions[_trans] ) { - case 1: -#line 38 "hb-buffer-deserialize-text.rl" - { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); -} - break; - case 3: -#line 51 "hb-buffer-deserialize-text.rl" - { - tok = p; -} - break; - case 5: -#line 55 "hb-buffer-deserialize-text.rl" - { if (unlikely (!buffer->ensure_glyphs ())) return false; } - break; - case 8: -#line 56 "hb-buffer-deserialize-text.rl" - { if (unlikely (!buffer->ensure_unicode ())) return false; } - break; - case 18: -#line 58 "hb-buffer-deserialize-text.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} - break; - case 9: -#line 66 "hb-buffer-deserialize-text.rl" - {if (!parse_hex (tok, p, &info.codepoint )) return false; } - break; - case 21: -#line 68 "hb-buffer-deserialize-text.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } - break; - case 6: -#line 69 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.x_offset )) return false; } - break; - case 23: -#line 70 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } - break; - case 20: -#line 71 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } - break; - case 15: -#line 38 "hb-buffer-deserialize-text.rl" - { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); -} -#line 51 "hb-buffer-deserialize-text.rl" - { - tok = p; -} - break; - case 4: -#line 51 "hb-buffer-deserialize-text.rl" - { - tok = p; -} -#line 55 "hb-buffer-deserialize-text.rl" - { if (unlikely (!buffer->ensure_glyphs ())) return false; } - break; - case 2: -#line 51 "hb-buffer-deserialize-text.rl" - { - tok = p; -} -#line 56 "hb-buffer-deserialize-text.rl" - { if (unlikely (!buffer->ensure_unicode ())) return false; } - break; - case 16: -#line 58 "hb-buffer-deserialize-text.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 7: -#line 66 "hb-buffer-deserialize-text.rl" - {if (!parse_hex (tok, p, &info.codepoint )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 10: -#line 68 "hb-buffer-deserialize-text.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 22: -#line 70 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 19: -#line 71 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 24: -#line 72 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.y_advance)) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 12: -#line 38 "hb-buffer-deserialize-text.rl" - { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); -} -#line 51 "hb-buffer-deserialize-text.rl" - { - tok = p; -} -#line 55 "hb-buffer-deserialize-text.rl" - { if (unlikely (!buffer->ensure_glyphs ())) return false; } - break; - case 14: -#line 38 "hb-buffer-deserialize-text.rl" - { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); -} -#line 51 "hb-buffer-deserialize-text.rl" - { - tok = p; -} -#line 58 "hb-buffer-deserialize-text.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} - break; - case 17: -#line 58 "hb-buffer-deserialize-text.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 55 "hb-buffer-deserialize-text.rl" - { if (unlikely (!buffer->ensure_glyphs ())) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 11: -#line 38 "hb-buffer-deserialize-text.rl" - { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); -} -#line 51 "hb-buffer-deserialize-text.rl" - { - tok = p; -} -#line 58 "hb-buffer-deserialize-text.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 13: -#line 38 "hb-buffer-deserialize-text.rl" - { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); -} -#line 51 "hb-buffer-deserialize-text.rl" - { - tok = p; -} -#line 58 "hb-buffer-deserialize-text.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 55 "hb-buffer-deserialize-text.rl" - { if (unlikely (!buffer->ensure_glyphs ())) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; -#line 722 "hb-buffer-deserialize-text.hh" - } - -_again: - if ( cs == 0 ) - goto _out; - if ( ++p != pe ) - goto _resume; - _test_eof: {} - if ( p == eof ) - { - switch ( _deserialize_text_eof_actions[cs] ) { - case 16: -#line 58 "hb-buffer-deserialize-text.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 7: -#line 66 "hb-buffer-deserialize-text.rl" - {if (!parse_hex (tok, p, &info.codepoint )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 10: -#line 68 "hb-buffer-deserialize-text.rl" - { if (!parse_uint (tok, p, &info.cluster )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 22: -#line 70 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.y_offset )) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 19: -#line 71 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.x_advance)) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 24: -#line 72 "hb-buffer-deserialize-text.rl" - { if (!parse_int (tok, p, &pos.y_advance)) return false; } -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; - case 11: -#line 38 "hb-buffer-deserialize-text.rl" - { - memset (&info, 0, sizeof (info)); - memset (&pos , 0, sizeof (pos )); -} -#line 51 "hb-buffer-deserialize-text.rl" - { - tok = p; -} -#line 58 "hb-buffer-deserialize-text.rl" - { - /* TODO Unescape delimiters. */ - if (!hb_font_glyph_from_string (font, - tok, p - tok, - &info.codepoint)) - return false; -} -#line 43 "hb-buffer-deserialize-text.rl" - { - buffer->add_info (info); - if (unlikely (!buffer->successful)) - return false; - buffer->pos[buffer->len - 1] = pos; - *end_ptr = p; -} - break; -#line 839 "hb-buffer-deserialize-text.hh" - } - } - - _out: {} - } - -#line 138 "hb-buffer-deserialize-text.rl" - - - *end_ptr = p; - - return p == pe && *(p-1) != ']'; -} - -#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc index 4797d6a79fb..bb2cddcb655 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc @@ -56,7 +56,7 @@ hb_buffer_serialize_list_formats () /** * hb_buffer_serialize_format_from_string: * @str: (array length=len) (element-type uint8_t): a string to parse - * @len: length of @str, or -1 if string is %NULL terminated + * @len: length of @str, or -1 if string is `NULL` terminated * * Parses a string into an #hb_buffer_serialize_format_t. Does not check if * @str is a valid buffer serialization format, use @@ -78,11 +78,11 @@ hb_buffer_serialize_format_from_string (const char *str, int len) * hb_buffer_serialize_format_to_string: * @format: an #hb_buffer_serialize_format_t to convert. * - * Converts @format to the string corresponding it, or %NULL if it is not a valid + * Converts @format to the string corresponding it, or `NULL` if it is not a valid * #hb_buffer_serialize_format_t. * * Return value: (transfer none): - * A %NULL terminated string corresponding to @format. Should not be freed. + * A `NULL` terminated string corresponding to @format. Should not be freed. * * Since: 0.9.7 **/ @@ -183,7 +183,7 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, unsigned int l = p - b; if (buf_size > l) { - memcpy (buf, b, l); + hb_memcpy (buf, b, l); buf += l; buf_size -= l; *buf_consumed += l; @@ -241,7 +241,7 @@ _hb_buffer_serialize_unicode_json (hb_buffer_t *buffer, unsigned int l = p - b; if (buf_size > l) { - memcpy (buf, b, l); + hb_memcpy (buf, b, l); buf += l; buf_size -= l; *buf_consumed += l; @@ -329,7 +329,7 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, unsigned int l = p - b; if (buf_size > l) { - memcpy (buf, b, l); + hb_memcpy (buf, b, l); buf += l; buf_size -= l; *buf_consumed += l; @@ -381,7 +381,7 @@ _hb_buffer_serialize_unicode_text (hb_buffer_t *buffer, unsigned int l = p - b; if (buf_size > l) { - memcpy (buf, b, l); + hb_memcpy (buf, b, l); buf += l; buf_size -= l; *buf_consumed += l; @@ -400,9 +400,9 @@ _hb_buffer_serialize_unicode_text (hb_buffer_t *buffer, * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to * write serialized buffer into. * @buf_size: the size of @buf. - * @buf_consumed: (out) (optional): if not %NULL, will be set to the number of bytes written into @buf. + * @buf_consumed: (out) (optional): if not `NULL`, will be set to the number of bytes written into @buf. * @font: (nullable): the #hb_font_t used to shape this buffer, needed to - * read glyph names and extents. If %NULL, an empty font will be used. + * read glyph names and extents. If `NULL`, an empty font will be used. * @format: the #hb_buffer_serialize_format_t to use for formatting the output. * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties * to serialize. @@ -514,7 +514,7 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to * write serialized buffer into. * @buf_size: the size of @buf. - * @buf_consumed: (out) (optional): if not %NULL, will be set to the number of bytes written into @buf. + * @buf_consumed: (out) (optional): if not `NULL`, will be set to the number of bytes written into @buf. * @format: the #hb_buffer_serialize_format_t to use for formatting the output. * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties * to serialize. @@ -637,9 +637,9 @@ _hb_buffer_serialize_invalid (hb_buffer_t *buffer, * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to * write serialized buffer into. * @buf_size: the size of @buf. - * @buf_consumed: (out) (optional): if not %NULL, will be set to the number of bytes written into @buf. + * @buf_consumed: (out) (optional): if not `NULL`, will be set to the number of bytes written into @buf. * @font: (nullable): the #hb_font_t used to shape this buffer, needed to - * read glyph names and extents. If %NULL, an empty font will be used. + * read glyph names and extents. If `NULL`, an empty font will be used. * @format: the #hb_buffer_serialize_format_t to use for formatting the output. * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties * to serialize. @@ -721,13 +721,14 @@ parse_hex (const char *pp, const char *end, uint32_t *pv) } #include "hb-buffer-deserialize-json.hh" -#include "hb-buffer-deserialize-text.hh" +#include "hb-buffer-deserialize-text-glyphs.hh" +#include "hb-buffer-deserialize-text-unicode.hh" /** * hb_buffer_deserialize_glyphs: * @buffer: an #hb_buffer_t buffer. * @buf: (array length=buf_len): string to deserialize - * @buf_len: the size of @buf, or -1 if it is %NULL-terminated + * @buf_len: the size of @buf, or -1 if it is `NULL`-terminated * @end_ptr: (out) (optional): output pointer to the character after last * consumed one. * @font: (nullable): font for getting glyph IDs @@ -736,7 +737,8 @@ parse_hex (const char *pp, const char *end, uint32_t *pv) * Deserializes glyphs @buffer from textual representation in the format * produced by hb_buffer_serialize_glyphs(). * - * Return value: %true if @buf is not fully consumed, %false otherwise. + * Return value: `true` if parse was successful, `false` if an error + * occurred. * * Since: 0.9.7 **/ @@ -779,9 +781,9 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, switch (format) { case HB_BUFFER_SERIALIZE_FORMAT_TEXT: - return _hb_buffer_deserialize_text (buffer, - buf, buf_len, end_ptr, - font); + return _hb_buffer_deserialize_text_glyphs (buffer, + buf, buf_len, end_ptr, + font); case HB_BUFFER_SERIALIZE_FORMAT_JSON: return _hb_buffer_deserialize_json (buffer, @@ -800,7 +802,7 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, * hb_buffer_deserialize_unicode: * @buffer: an #hb_buffer_t buffer. * @buf: (array length=buf_len): string to deserialize - * @buf_len: the size of @buf, or -1 if it is %NULL-terminated + * @buf_len: the size of @buf, or -1 if it is `NULL`-terminated * @end_ptr: (out) (optional): output pointer to the character after last * consumed one. * @format: the #hb_buffer_serialize_format_t of the input @buf @@ -808,7 +810,8 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, * Deserializes Unicode @buffer from textual representation in the format * produced by hb_buffer_serialize_unicode(). * - * Return value: %true if @buf is not fully consumed, %false otherwise. + * Return value: `true` if parse was successful, `false` if an error + * occurred. * * Since: 2.7.3 **/ @@ -849,9 +852,9 @@ hb_buffer_deserialize_unicode (hb_buffer_t *buffer, switch (format) { case HB_BUFFER_SERIALIZE_FORMAT_TEXT: - return _hb_buffer_deserialize_text (buffer, - buf, buf_len, end_ptr, - font); + return _hb_buffer_deserialize_text_unicode (buffer, + buf, buf_len, end_ptr, + font); case HB_BUFFER_SERIALIZE_FORMAT_JSON: return _hb_buffer_deserialize_json (buffer, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc index 1b96fafb0d4..f2fef3137e5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc @@ -150,7 +150,7 @@ buffer_verify_unsafe_to_break (hb_buffer_t *buffer, assert (text_start < text_end); if (0) - printf("start %d end %d text start %d end %d\n", start, end, text_start, text_end); + printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end); hb_buffer_clear_contents (fragment); @@ -186,7 +186,7 @@ buffer_verify_unsafe_to_break (hb_buffer_t *buffer, bool ret = true; hb_buffer_diff_flags_t diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0); - if (diff) + if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH) { buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-break test failed."); ret = false; @@ -292,7 +292,7 @@ buffer_verify_unsafe_to_concat (hb_buffer_t *buffer, assert (text_start < text_end); if (0) - printf("start %d end %d text start %d end %d\n", start, end, text_start, text_end); + printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end); #if 0 hb_buffer_flags_t flags = hb_buffer_get_flags (fragment); @@ -313,7 +313,6 @@ buffer_verify_unsafe_to_concat (hb_buffer_t *buffer, bool ret = true; hb_buffer_diff_flags_t diff; - /* * Shape the two fragment streams. */ @@ -382,7 +381,7 @@ buffer_verify_unsafe_to_concat (hb_buffer_t *buffer, * Diff results. */ diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0); - if (diff) + if (diff & ~HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH) { buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-concat test failed."); ret = false; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc index e743e18125a..3532917631d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc @@ -51,7 +51,7 @@ * Checks the equality of two #hb_segment_properties_t's. * * Return value: - * %true if all properties of @a equal those of @b, %false otherwise. + * `true` if all properties of @a equal those of @b, `false` otherwise. * * Since: 0.9.7 **/ @@ -172,12 +172,13 @@ hb_buffer_t::enlarge (unsigned int size) while (size >= new_allocated) new_allocated += (new_allocated >> 1) + 32; - static_assert ((sizeof (info[0]) == sizeof (pos[0])), ""); - if (unlikely (hb_unsigned_mul_overflows (new_allocated, sizeof (info[0])))) + unsigned new_bytes; + if (unlikely (hb_unsigned_mul_overflows (new_allocated, sizeof (info[0]), &new_bytes))) goto done; - new_pos = (hb_glyph_position_t *) hb_realloc (pos, new_allocated * sizeof (pos[0])); - new_info = (hb_glyph_info_t *) hb_realloc (info, new_allocated * sizeof (info[0])); + static_assert (sizeof (info[0]) == sizeof (pos[0]), ""); + new_pos = (hb_glyph_position_t *) hb_realloc (pos, new_bytes); + new_info = (hb_glyph_info_t *) hb_realloc (info, new_bytes); done: if (unlikely (!new_pos || !new_info)) @@ -208,7 +209,7 @@ hb_buffer_t::make_room_for (unsigned int num_in, assert (have_output); out_info = (hb_glyph_info_t *) pos; - memcpy (out_info, info, out_len * sizeof (out_info[0])); + hb_memcpy (out_info, info, out_len * sizeof (out_info[0])); } return true; @@ -229,7 +230,7 @@ hb_buffer_t::shift_forward (unsigned int count) * Ideally, we should at least set Default_Ignorable bits on * these, as well as consistent cluster values. But the former * is layering violation... */ - memset (info + len, 0, (idx + count - len) * sizeof (info[0])); + hb_memset (info + len, 0, (idx + count - len) * sizeof (info[0])); } len += count; idx += count; @@ -298,8 +299,8 @@ hb_buffer_t::clear () out_len = 0; out_info = info; - memset (context, 0, sizeof context); - memset (context_len, 0, sizeof context_len); + hb_memset (context, 0, sizeof context); + hb_memset (context_len, 0, sizeof context_len); deallocate_var_all (); serial = 0; @@ -313,15 +314,14 @@ hb_buffer_t::enter () serial = 0; shaping_failed = false; scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; - if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_LEN_FACTOR))) + unsigned mul; + if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_LEN_FACTOR, &mul))) { - max_len = hb_max (len * HB_BUFFER_MAX_LEN_FACTOR, - (unsigned) HB_BUFFER_MAX_LEN_MIN); + max_len = hb_max (mul, (unsigned) HB_BUFFER_MAX_LEN_MIN); } - if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_OPS_FACTOR))) + if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_OPS_FACTOR, &mul))) { - max_ops = hb_max (len * HB_BUFFER_MAX_OPS_FACTOR, - (unsigned) HB_BUFFER_MAX_OPS_MIN); + max_ops = hb_max (mul, (unsigned) HB_BUFFER_MAX_OPS_MIN); } } void @@ -345,7 +345,7 @@ hb_buffer_t::add (hb_codepoint_t codepoint, glyph = &info[len]; - memset (glyph, 0, sizeof (*glyph)); + hb_memset (glyph, 0, sizeof (*glyph)); glyph->codepoint = codepoint; glyph->mask = 0; glyph->cluster = cluster; @@ -387,9 +387,11 @@ hb_buffer_t::clear_positions () hb_memset (pos, 0, sizeof (pos[0]) * len); } -void +bool hb_buffer_t::sync () { + bool ret = false; + assert (have_output); assert (idx <= len); @@ -403,12 +405,39 @@ hb_buffer_t::sync () info = out_info; } len = out_len; + ret = true; reset: have_output = false; out_len = 0; out_info = info; idx = 0; + + return ret; +} + +int +hb_buffer_t::sync_so_far () +{ + bool had_output = have_output; + unsigned out_i = out_len; + unsigned i = idx; + unsigned old_idx = idx; + + if (sync ()) + idx = out_i; + else + idx = i; + + if (had_output) + { + have_output = true; + out_len = idx; + } + + assert (idx <= len); + + return idx - old_idx; } bool @@ -493,15 +522,17 @@ hb_buffer_t::merge_clusters_impl (unsigned int start, cluster = hb_min (cluster, info[i].cluster); /* Extend end */ - while (end < len && info[end - 1].cluster == info[end].cluster) - end++; + if (cluster != info[end - 1].cluster) + while (end < len && info[end - 1].cluster == info[end].cluster) + end++; /* Extend start */ - while (idx < start && info[start - 1].cluster == info[start].cluster) - start--; + if (cluster != info[start].cluster) + while (idx < start && info[start - 1].cluster == info[start].cluster) + start--; /* If we hit the start of buffer, continue in out-buffer. */ - if (idx == start) + if (idx == start && info[start].cluster != cluster) for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--) set_cluster (out_info[i - 1], cluster); @@ -576,6 +607,53 @@ hb_buffer_t::delete_glyph () skip_glyph (); } +void +hb_buffer_t::delete_glyphs_inplace (bool (*filter) (const hb_glyph_info_t *info)) +{ + /* Merge clusters and delete filtered glyphs. + * NOTE! We can't use out-buffer as we have positioning data. */ + unsigned int j = 0; + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + { + if (filter (&info[i])) + { + /* Merge clusters. + * Same logic as delete_glyph(), but for in-place removal. */ + + unsigned int cluster = info[i].cluster; + if (i + 1 < count && cluster == info[i + 1].cluster) + continue; /* Cluster survives; do nothing. */ + + if (j) + { + /* Merge cluster backward. */ + if (cluster < info[j - 1].cluster) + { + unsigned int mask = info[i].mask; + unsigned int old_cluster = info[j - 1].cluster; + for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--) + set_cluster (info[k - 1], cluster, mask); + } + continue; + } + + if (i + 1 < count) + merge_clusters (i, i + 2); /* Merge cluster forward. */ + + continue; + } + + if (j != i) + { + info[j] = info[i]; + pos[j] = pos[i]; + } + j++; + } + len = j; +} + void hb_buffer_t::guess_segment_properties () { @@ -643,9 +721,9 @@ DEFINE_NULL_INSTANCE (hb_buffer_t) = * Return value: (transfer full): * A newly allocated #hb_buffer_t with a reference count of 1. The initial * reference count should be released with hb_buffer_destroy() when you are done - * using the #hb_buffer_t. This function never returns %NULL. If memory cannot + * using the #hb_buffer_t. This function never returns `NULL`. If memory cannot * be allocated, a special #hb_buffer_t object will be returned on which - * hb_buffer_allocation_successful() returns %false. + * hb_buffer_allocation_successful() returns `false`. * * Since: 0.9.2 **/ @@ -775,7 +853,7 @@ hb_buffer_destroy (hb_buffer_t *buffer) * * Attaches a user-data key/data pair to the specified buffer. * - * Return value: %true if success, %false otherwise + * Return value: `true` if success, `false` otherwise * * Since: 0.9.2 **/ @@ -802,7 +880,7 @@ hb_buffer_set_user_data (hb_buffer_t *buffer, * Since: 0.9.2 **/ void * -hb_buffer_get_user_data (hb_buffer_t *buffer, +hb_buffer_get_user_data (const hb_buffer_t *buffer, hb_user_data_key_t *key) { return hb_object_get_user_data (buffer, key); @@ -817,6 +895,32 @@ hb_buffer_get_user_data (hb_buffer_t *buffer, * Sets the type of @buffer contents. Buffers are either empty, contain * characters (before shaping), or contain glyphs (the result of shaping). * + * You rarely need to call this function, since a number of other + * functions transition the content type for you. Namely: + * + * - A newly created buffer starts with content type + * %HB_BUFFER_CONTENT_TYPE_INVALID. Calling hb_buffer_reset(), + * hb_buffer_clear_contents(), as well as calling hb_buffer_set_length() + * with an argument of zero all set the buffer content type to invalid + * as well. + * + * - Calling hb_buffer_add_utf8(), hb_buffer_add_utf16(), + * hb_buffer_add_utf32(), hb_buffer_add_codepoints() and + * hb_buffer_add_latin1() expect that buffer is either empty and + * have a content type of invalid, or that buffer content type is + * %HB_BUFFER_CONTENT_TYPE_UNICODE, and they also set the content + * type to Unicode if they added anything to an empty buffer. + * + * - Finally hb_shape() and hb_shape_full() expect that the buffer + * is either empty and have content type of invalid, or that buffer + * content type is %HB_BUFFER_CONTENT_TYPE_UNICODE, and upon + * success they set the buffer content type to + * %HB_BUFFER_CONTENT_TYPE_GLYPHS. + * + * The above transitions are designed such that one can use a buffer + * in a loop of "reset : add-text : shape" without needing to ever + * modify the content type manually. + * * Since: 0.9.5 **/ void @@ -904,7 +1008,6 @@ hb_buffer_get_unicode_funcs (const hb_buffer_t *buffer) void hb_buffer_set_direction (hb_buffer_t *buffer, hb_direction_t direction) - { if (unlikely (hb_object_is_immutable (buffer))) return; @@ -1278,7 +1381,7 @@ hb_buffer_clear_contents (hb_buffer_t *buffer) * Pre allocates memory for @buffer to fit at least @size number of items. * * Return value: - * %true if @buffer memory allocation succeeded, %false otherwise + * `true` if @buffer memory allocation succeeded, `false` otherwise * * Since: 0.9.2 **/ @@ -1295,7 +1398,7 @@ hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size) * Check if allocating memory for the buffer succeeded. * * Return value: - * %true if @buffer memory allocation succeeded, %false otherwise. + * `true` if @buffer memory allocation succeeded, `false` otherwise. * * Since: 0.9.2 **/ @@ -1340,7 +1443,7 @@ hb_buffer_add (hb_buffer_t *buffer, * end. * * Return value: - * %true if @buffer memory allocation succeeded, %false otherwise. + * `true` if @buffer memory allocation succeeded, `false` otherwise. * * Since: 0.9.2 **/ @@ -1356,9 +1459,9 @@ hb_buffer_set_length (hb_buffer_t *buffer, /* Wipe the new space */ if (length > buffer->len) { - memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len)); + hb_memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len)); if (buffer->have_positions) - memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len)); + hb_memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len)); } buffer->len = length; @@ -1426,7 +1529,7 @@ hb_buffer_get_glyph_infos (hb_buffer_t *buffer, * If buffer did not have positions before, the positions will be * initialized to zeros, unless this function is called from * within a buffer message callback (see hb_buffer_set_message_func()), - * in which case %NULL is returned. + * in which case `NULL` is returned. * * Return value: (transfer none) (array length=length): * The @buffer glyph position array. @@ -1461,7 +1564,7 @@ hb_buffer_get_glyph_positions (hb_buffer_t *buffer, * and cleared of position data when hb_buffer_clear_contents() is called. * * Return value: - * %true if the @buffer has position array, %false otherwise. + * `true` if the @buffer has position array, `false` otherwise. * * Since: 2.7.3 **/ @@ -1645,10 +1748,10 @@ hb_buffer_add_utf (hb_buffer_t *buffer, * @buffer: An #hb_buffer_t * @text: (array length=text_length) (element-type uint8_t): An array of UTF-8 * characters to append. - * @text_length: The length of the @text, or -1 if it is %NULL terminated. + * @text_length: The length of the @text, or -1 if it is `NULL` terminated. * @item_offset: The offset of the first character to add to the @buffer. * @item_length: The number of characters to add to the @buffer, or -1 for the - * end of @text (assuming it is %NULL terminated). + * end of @text (assuming it is `NULL` terminated). * * See hb_buffer_add_codepoints(). * @@ -1671,10 +1774,10 @@ hb_buffer_add_utf8 (hb_buffer_t *buffer, * hb_buffer_add_utf16: * @buffer: An #hb_buffer_t * @text: (array length=text_length): An array of UTF-16 characters to append - * @text_length: The length of the @text, or -1 if it is %NULL terminated + * @text_length: The length of the @text, or -1 if it is `NULL` terminated * @item_offset: The offset of the first character to add to the @buffer * @item_length: The number of characters to add to the @buffer, or -1 for the - * end of @text (assuming it is %NULL terminated) + * end of @text (assuming it is `NULL` terminated) * * See hb_buffer_add_codepoints(). * @@ -1697,10 +1800,10 @@ hb_buffer_add_utf16 (hb_buffer_t *buffer, * hb_buffer_add_utf32: * @buffer: An #hb_buffer_t * @text: (array length=text_length): An array of UTF-32 characters to append - * @text_length: The length of the @text, or -1 if it is %NULL terminated + * @text_length: The length of the @text, or -1 if it is `NULL` terminated * @item_offset: The offset of the first character to add to the @buffer * @item_length: The number of characters to add to the @buffer, or -1 for the - * end of @text (assuming it is %NULL terminated) + * end of @text (assuming it is `NULL` terminated) * * See hb_buffer_add_codepoints(). * @@ -1724,10 +1827,10 @@ hb_buffer_add_utf32 (hb_buffer_t *buffer, * @buffer: An #hb_buffer_t * @text: (array length=text_length) (element-type uint8_t): an array of UTF-8 * characters to append - * @text_length: the length of the @text, or -1 if it is %NULL terminated + * @text_length: the length of the @text, or -1 if it is `NULL` terminated * @item_offset: the offset of the first character to add to the @buffer * @item_length: the number of characters to add to the @buffer, or -1 for the - * end of @text (assuming it is %NULL terminated) + * end of @text (assuming it is `NULL` terminated) * * Similar to hb_buffer_add_codepoints(), but allows only access to first 256 * Unicode code points that can fit in 8-bit strings. @@ -1750,10 +1853,10 @@ hb_buffer_add_latin1 (hb_buffer_t *buffer, * hb_buffer_add_codepoints: * @buffer: a #hb_buffer_t to append characters to. * @text: (array length=text_length): an array of Unicode code points to append. - * @text_length: the length of the @text, or -1 if it is %NULL terminated. + * @text_length: the length of the @text, or -1 if it is `NULL` terminated. * @item_offset: the offset of the first code point to add to the @buffer. * @item_length: the number of code points to add to the @buffer, or -1 for the - * end of @text (assuming it is %NULL terminated). + * end of @text (assuming it is `NULL` terminated). * * Appends characters from @text array to @buffer. The @item_offset is the * position of the first character from @text that will be appended, and @@ -1766,7 +1869,9 @@ hb_buffer_add_latin1 (hb_buffer_t *buffer, * marks at stat of run. * * This function does not check the validity of @text, it is up to the caller - * to ensure it contains a valid Unicode code points. + * to ensure it contains a valid Unicode scalar values. In contrast, + * hb_buffer_add_utf32() can be used that takes similar input but performs + * sanity-check on the input. * * Since: 0.9.31 **/ @@ -1829,9 +1934,9 @@ hb_buffer_append (hb_buffer_t *buffer, hb_segment_properties_overlay (&buffer->props, &source->props); - memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0])); + hb_memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0])); if (buffer->have_positions) - memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0])); + hb_memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0])); if (source->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) { @@ -2019,7 +2124,7 @@ hb_buffer_diff (hb_buffer_t *buffer, result |= HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH; if (buf_info->cluster != ref_info->cluster) result |= HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH; - if ((buf_info->mask & ~ref_info->mask & HB_GLYPH_FLAG_DEFINED)) + if ((buf_info->mask ^ ref_info->mask) & HB_GLYPH_FLAG_DEFINED) result |= HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH; if (contains && ref_info->codepoint == dottedcircle_glyph) result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT; @@ -2074,6 +2179,13 @@ hb_buffer_set_message_func (hb_buffer_t *buffer, hb_buffer_message_func_t func, void *user_data, hb_destroy_func_t destroy) { + if (unlikely (hb_object_is_immutable (buffer))) + { + if (destroy) + destroy (user_data); + return; + } + if (buffer->message_destroy) buffer->message_destroy (buffer->message_data); @@ -2090,8 +2202,16 @@ hb_buffer_set_message_func (hb_buffer_t *buffer, bool hb_buffer_t::message_impl (hb_font_t *font, const char *fmt, va_list ap) { + assert (!have_output || (out_info == info && out_len == idx)); + + message_depth++; + char buf[100]; vsnprintf (buf, sizeof (buf), fmt, ap); - return (bool) this->message_func (this, font, buf, this->message_data); + bool ret = (bool) this->message_func (this, font, buf, this->message_data); + + message_depth--; + + return ret; } #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.h b/src/java.desktop/share/native/libharfbuzz/hb-buffer.h index d5c2b25f852..9b21ffb10fa 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.h @@ -142,6 +142,15 @@ typedef struct hb_glyph_info_t { * shaping, otherwise the buffer flag will not be * reliably produced. * Since: 4.0.0 + * @HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL: In scripts that use elongation (Arabic, + Mongolian, Syriac, etc.), this flag signifies + that it is safe to insert a U+0640 TATWEEL + character before this cluster for elongation. + This flag does not determine the + script-specific elongation places, but only + when it is safe to do the elongation without + interrupting text shaping. + Since: 5.1.0 * @HB_GLYPH_FLAG_DEFINED: All the currently defined flags. * * Flags for #hb_glyph_info_t. @@ -149,10 +158,11 @@ typedef struct hb_glyph_info_t { * Since: 1.5.0 */ typedef enum { /*< flags >*/ - HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001, - HB_GLYPH_FLAG_UNSAFE_TO_CONCAT = 0x00000002, + HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001, + HB_GLYPH_FLAG_UNSAFE_TO_CONCAT = 0x00000002, + HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL = 0x00000004, - HB_GLYPH_FLAG_DEFINED = 0x00000003 /* OR of all defined flags */ + HB_GLYPH_FLAG_DEFINED = 0x00000007 /* OR of all defined flags */ } hb_glyph_flags_t; HB_EXTERN hb_glyph_flags_t @@ -266,7 +276,7 @@ hb_buffer_set_user_data (hb_buffer_t *buffer, hb_bool_t replace); HB_EXTERN void * -hb_buffer_get_user_data (hb_buffer_t *buffer, +hb_buffer_get_user_data (const hb_buffer_t *buffer, hb_user_data_key_t *key); @@ -373,6 +383,10 @@ hb_buffer_guess_segment_properties (hb_buffer_t *buffer); * flag indicating that the @HB_GLYPH_FLAG_UNSAFE_TO_CONCAT * glyph-flag should be produced by the shaper. By default * it will not be produced since it incurs a cost. Since: 4.0.0 + * @HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL: + * flag indicating that the @HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL + * glyph-flag should be produced by the shaper. By default + * it will not be produced. Since: 5.1.0 * @HB_BUFFER_FLAG_DEFINED: All currently defined flags: Since: 4.4.0 * * Flags for #hb_buffer_t. @@ -388,8 +402,9 @@ typedef enum { /*< flags >*/ HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE = 0x00000010u, HB_BUFFER_FLAG_VERIFY = 0x00000020u, HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT = 0x00000040u, + HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL = 0x00000080u, - HB_BUFFER_FLAG_DEFINED = 0x0000007Fu + HB_BUFFER_FLAG_DEFINED = 0x000000FFu } hb_buffer_flags_t; HB_EXTERN void @@ -748,23 +763,23 @@ hb_buffer_diff (hb_buffer_t *buffer, /* - * Debugging. + * Tracing. */ /** * hb_buffer_message_func_t: * @buffer: An #hb_buffer_t to work upon * @font: The #hb_font_t the @buffer is shaped with - * @message: %NULL-terminated message passed to the function + * @message: `NULL`-terminated message passed to the function * @user_data: User data pointer passed by the caller * * A callback method for #hb_buffer_t. The method gets called with the * #hb_buffer_t it was set on, the #hb_font_t the buffer is shaped with and a * message describing what step of the shaping process will be performed. - * Returning %false from this method will skip this shaping step and move to + * Returning `false` from this method will skip this shaping step and move to * the next one. * - * Return value: %true to perform the shaping step, %false to skip it. + * Return value: `true` to perform the shaping step, `false` to skip it. * * Since: 1.1.3 */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh index 44f30d0a5dc..c1476364de5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh @@ -32,28 +32,9 @@ #include "hb.hh" #include "hb-unicode.hh" +#include "hb-set-digest.hh" -#ifndef HB_BUFFER_MAX_LEN_FACTOR -#define HB_BUFFER_MAX_LEN_FACTOR 64 -#endif -#ifndef HB_BUFFER_MAX_LEN_MIN -#define HB_BUFFER_MAX_LEN_MIN 16384 -#endif -#ifndef HB_BUFFER_MAX_LEN_DEFAULT -#define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */ -#endif - -#ifndef HB_BUFFER_MAX_OPS_FACTOR -#define HB_BUFFER_MAX_OPS_FACTOR 1024 -#endif -#ifndef HB_BUFFER_MAX_OPS_MIN -#define HB_BUFFER_MAX_OPS_MIN 16384 -#endif -#ifndef HB_BUFFER_MAX_OPS_DEFAULT -#define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */ -#endif - static_assert ((sizeof (hb_glyph_info_t) == 20), ""); static_assert ((sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)), ""); @@ -207,6 +188,14 @@ struct hb_buffer_t hb_glyph_info_t &prev () { return out_info[out_len ? out_len - 1 : 0]; } hb_glyph_info_t prev () const { return out_info[out_len ? out_len - 1 : 0]; } + hb_set_digest_t digest () const + { + hb_set_digest_t d; + d.init (); + d.add_array (&info[0].codepoint, len, sizeof (info[0])); + return d; + } + HB_INTERNAL void similar (const hb_buffer_t &src); HB_INTERNAL void reset (); HB_INTERNAL void clear (); @@ -288,7 +277,8 @@ struct hb_buffer_t HB_INTERNAL void guess_segment_properties (); - HB_INTERNAL void sync (); + HB_INTERNAL bool sync (); + HB_INTERNAL int sync_so_far (); HB_INTERNAL void clear_output (); HB_INTERNAL void clear_positions (); @@ -401,6 +391,8 @@ struct hb_buffer_t HB_INTERNAL void merge_out_clusters (unsigned int start, unsigned int end); /* Merge clusters for deleting current glyph, and skip it. */ HB_INTERNAL void delete_glyph (); + HB_INTERNAL void delete_glyphs_inplace (bool (*filter) (const hb_glyph_info_t *info)); + /* Adds glyph flags in mask to infos with clusters between start and end. @@ -461,6 +453,17 @@ struct hb_buffer_t start, end, true); } + void safe_to_insert_tatweel (unsigned int start = 0, unsigned int end = -1) + { + if ((flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL) == 0) + { + unsafe_to_break (start, end); + return; + } + _set_glyph_flags (HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL, + start, end, + true); + } void unsafe_to_concat (unsigned int start = 0, unsigned int end = -1) { if (likely ((flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0)) @@ -555,15 +558,11 @@ struct hb_buffer_t if (likely (!messaging ())) return true; - message_depth++; - va_list ap; va_start (ap, fmt); bool ret = message_impl (font, fmt, ap); va_end (ap); - message_depth--; - return ret; #endif } @@ -582,21 +581,59 @@ struct hb_buffer_t unsigned int cluster, hb_mask_t mask) { - for (unsigned int i = start; i < end; i++) - if (cluster != infos[i].cluster) + if (unlikely (start == end)) + return; + + unsigned cluster_first = infos[start].cluster; + unsigned cluster_last = infos[end - 1].cluster; + + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS || + (cluster != cluster_first && cluster != cluster_last)) + { + for (unsigned int i = start; i < end; i++) + if (cluster != infos[i].cluster) + { + scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; + infos[i].mask |= mask; + } + return; + } + + /* Monotone clusters */ + + if (cluster == cluster_first) + { + for (unsigned int i = end; start < i && infos[i - 1].cluster != cluster_first; i--) + { + scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; + infos[i - 1].mask |= mask; + } + } + else /* cluster == cluster_last */ + { + for (unsigned int i = start; i < end && infos[i].cluster != cluster_last; i++) { scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; infos[i].mask |= mask; } + } } - static unsigned + unsigned _infos_find_min_cluster (const hb_glyph_info_t *infos, unsigned start, unsigned end, unsigned cluster = UINT_MAX) { - for (unsigned int i = start; i < end; i++) - cluster = hb_min (cluster, infos[i].cluster); - return cluster; + if (unlikely (start == end)) + return cluster; + + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + { + for (unsigned int i = start; i < end; i++) + cluster = hb_min (cluster, infos[i].cluster); + return cluster; + } + + return hb_min (cluster, hb_min (infos[start].cluster, infos[end - 1].cluster)); } void clear_glyph_flags (hb_mask_t mask = 0) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cache.hh b/src/java.desktop/share/native/libharfbuzz/hb-cache.hh index de8a808ec45..eb4a510fbf6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cache.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cache.hh @@ -32,27 +32,39 @@ /* Implements a lockfree cache for int->int functions. */ -template +template struct hb_cache_t { + using item_t = typename std::conditional::type, + typename std::conditional::type + >::type; + static_assert ((key_bits >= cache_bits), ""); - static_assert ((key_bits + value_bits - cache_bits <= 8 * sizeof (hb_atomic_int_t)), ""); - static_assert (sizeof (hb_atomic_int_t) == sizeof (unsigned int), ""); + static_assert ((key_bits + value_bits <= cache_bits + 8 * sizeof (item_t)), ""); + + hb_cache_t () { init (); } void init () { clear (); } - void fini () {} void clear () { for (unsigned i = 0; i < ARRAY_LENGTH (values); i++) - values[i].set_relaxed (-1); + values[i] = -1; } bool get (unsigned int key, unsigned int *value) const { unsigned int k = key & ((1u<> value_bits) != (key >> cache_bits)) return false; *value = v & ((1u<>cache_bits)< hb_cmap_cache_t; -typedef hb_cache_t<16, 24, 8> hb_advance_cache_t; - #endif /* HB_CACHE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh index 2c980c60279..e92e8140fd0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh @@ -284,65 +284,56 @@ struct UnsizedByteStr : UnsizedArrayOf /* A byte string associated with the current offset and an error condition */ struct byte_str_ref_t { - byte_str_ref_t () { init (); } - - void init () - { - str = hb_ubytes_t (); - offset = 0; - error = false; - } - - void fini () {} + byte_str_ref_t () + : str () {} byte_str_ref_t (const hb_ubytes_t &str_, unsigned int offset_ = 0) - : str (str_), offset (offset_), error (false) {} + : str (str_) { set_offset (offset_); } void reset (const hb_ubytes_t &str_, unsigned int offset_ = 0) { str = str_; - offset = offset_; - error = false; + set_offset (offset_); } const unsigned char& operator [] (int i) { - if (unlikely ((unsigned int) (offset + i) >= str.length)) + if (unlikely ((unsigned int) (get_offset () + i) >= str.length)) { set_error (); return Null (unsigned char); } - return str[offset + i]; + return str.arrayZ[get_offset () + i]; } + unsigned char head_unchecked () const { return str.arrayZ[get_offset ()]; } + /* Conversion to hb_ubytes_t */ - operator hb_ubytes_t () const { return str.sub_array (offset, str.length - offset); } + operator hb_ubytes_t () const { return str.sub_array (get_offset ()); } hb_ubytes_t sub_array (unsigned int offset_, unsigned int len_) const { return str.sub_array (offset_, len_); } bool avail (unsigned int count=1) const - { return (!in_error () && offset + count <= str.length); } + { return get_offset () + count <= str.length; } void inc (unsigned int count=1) { - if (likely (!in_error () && (offset <= str.length) && (offset + count <= str.length))) - { - offset += count; - } - else - { - offset = str.length; - set_error (); - } + /* Automatically puts us in error if count is out-of-range. */ + set_offset (get_offset () + count); } - void set_error () { error = true; } - bool in_error () const { return error; } + /* We (ab)use ubytes backwards_length as a cursor (called offset), + * as well as to store error condition. */ - hb_ubytes_t str; - unsigned int offset; /* beginning of the sub-string within str */ + unsigned get_offset () const { return str.backwards_length; } + void set_offset (unsigned offset) { str.backwards_length = offset; } + + void set_error () { str.backwards_length = str.length + 1; } + bool in_error () const { return str.backwards_length > str.length; } + + unsigned total_size () const { return str.length; } protected: - bool error; + hb_ubytes_t str; }; using byte_str_array_t = hb_vector_t; @@ -491,8 +482,15 @@ struct arg_stack_t : cff_stack_t /* an operator prefixed by its operands in a byte string */ struct op_str_t { - hb_ubytes_t str; - op_code_t op; + /* This used to have a hb_ubytes_t. Using a pointer and length + * in a particular order, saves 8 bytes in this struct and more + * in our parsed_cs_op_t subclass. */ + + const unsigned char *ptr = nullptr; + + op_code_t op = OpCode_Invalid; + + uint8_t length = 0; }; /* base of OP_SERIALIZER */ @@ -503,9 +501,11 @@ struct op_serializer_t { TRACE_SERIALIZE (this); - HBUINT8 *d = c->allocate_size (opstr.str.length); + unsigned char *d = c->allocate_size (opstr.length); if (unlikely (!d)) return_trace (false); - memcpy (d, &opstr.str[0], opstr.str.length); + /* Faster than hb_memcpy for small strings. */ + for (unsigned i = 0; i < opstr.length; i++) + d[i] = opstr.ptr[i]; return_trace (true); } }; @@ -522,23 +522,17 @@ struct parsed_values_t void alloc (unsigned n) { - values.alloc (n); + values.alloc (n, true); } - void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t ()) - { - VAL *val = values.push (); - val->op = op; - val->str = str_ref.str.sub_array (opStart, str_ref.offset - opStart); - opStart = str_ref.offset; - } - - void add_op (op_code_t op, const byte_str_ref_t& str_ref, const VAL &v) + void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t (), const VAL &v = VAL ()) { VAL *val = values.push (v); val->op = op; - val->str = str_ref.sub_array ( opStart, str_ref.offset - opStart); - opStart = str_ref.offset; + auto arr = str_ref.sub_array (opStart, str_ref.get_offset () - opStart); + val->ptr = arr.arrayZ; + val->length = arr.length; + opStart = str_ref.get_offset (); } bool has_op (op_code_t op) const @@ -549,8 +543,7 @@ struct parsed_values_t } unsigned get_count () const { return values.length; } - const VAL &get_value (unsigned int i) const { return values[i]; } - const VAL &operator [] (unsigned int i) const { return get_value (i); } + const VAL &operator [] (unsigned int i) const { return values[i]; } unsigned int opStart; hb_vector_t values; @@ -565,23 +558,23 @@ struct interp_env_t str_ref.reset (str_); } bool in_error () const - { return error || str_ref.in_error () || argStack.in_error (); } + { return str_ref.in_error () || argStack.in_error (); } - void set_error () { error = true; } + void set_error () { str_ref.set_error (); } op_code_t fetch_op () { op_code_t op = OpCode_Invalid; if (unlikely (!str_ref.avail ())) return OpCode_Invalid; - op = (op_code_t)(unsigned char)str_ref[0]; + op = (op_code_t) str_ref.head_unchecked (); + str_ref.inc (); if (op == OpCode_escape) { if (unlikely (!str_ref.avail ())) return OpCode_Invalid; - op = Make_OpCode_ESC(str_ref[1]); + op = Make_OpCode_ESC (str_ref.head_unchecked ()); str_ref.inc (); } - str_ref.inc (); return op; } @@ -596,8 +589,6 @@ struct interp_env_t str_ref; arg_stack_t argStack; - protected: - bool error = false; }; using num_interp_env_t = interp_env_t<>; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh index f4f41370495..52b52c3f769 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh @@ -881,7 +881,13 @@ struct cs_interpreter_t : interpreter_t { SUPER::env.set_endchar (false); + unsigned max_ops = HB_CFF_MAX_OPS; for (;;) { + if (unlikely (!--max_ops)) + { + SUPER::env.set_error (); + break; + } OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param); if (unlikely (SUPER::env.in_error ())) return false; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh index 041585a89a6..0c0cec9986a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh @@ -35,10 +35,8 @@ using namespace OT; /* an opstr and the parsed out dict value(s) */ struct dict_val_t : op_str_t { - void init () { single_val.set_int (0); } + void init () {} void fini () {} - - number_t single_val; }; typedef dict_val_t num_dict_val_t; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh index cfcf442ee7d..fbb31c9ee16 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh @@ -38,7 +38,8 @@ typedef biased_subrs_t cff1_biased_subrs_t; struct cff1_cs_interp_env_t : cs_interp_env_t { template - cff1_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd) + cff1_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd, + const int *coords_=nullptr, unsigned int num_coords_=0) : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs) { processed_width = false; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh index 791f3aab56a..c970633e064 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh @@ -40,20 +40,22 @@ struct blend_arg_t : number_t void set_real (double v) { reset_blends (); number_t::set_real (v); } void set_blends (unsigned int numValues_, unsigned int valueIndex_, - unsigned int numBlends, hb_array_t blends_) + hb_array_t blends_) { numValues = numValues_; valueIndex = valueIndex_; - deltas.resize (numBlends); + unsigned numBlends = blends_.length; + if (unlikely (!deltas.resize_exact (numBlends))) + return; for (unsigned int i = 0; i < numBlends; i++) - deltas[i] = blends_[i]; + deltas.arrayZ[i] = blends_.arrayZ[i]; } bool blending () const { return deltas.length > 0; } void reset_blends () { numValues = valueIndex = 0; - deltas.resize (0); + deltas.shrink (0); } unsigned int numValues; @@ -61,7 +63,6 @@ struct blend_arg_t : number_t hb_vector_t deltas; }; -typedef interp_env_t BlendInterpEnv; typedef biased_subrs_t cff2_biased_subrs_t; template @@ -117,7 +118,7 @@ struct cff2_cs_interp_env_t : cs_interp_env_t region_count = varStore->varStore.get_region_index_count (get_ivs ()); if (do_blend) { - if (unlikely (!scalars.resize (region_count))) + if (unlikely (!scalars.resize_exact (region_count))) SUPER::set_error (); else varStore->varStore.get_region_scalars (get_ivs (), coords, num_coords, @@ -154,13 +155,16 @@ struct cff2_cs_interp_env_t : cs_interp_env_t { if (likely (scalars.length == deltas.length)) { - for (unsigned int i = 0; i < scalars.length; i++) - v += (double) scalars[i] * deltas[i].to_real (); + unsigned count = scalars.length; + for (unsigned i = 0; i < count; i++) + v += (double) scalars.arrayZ[i] * deltas.arrayZ[i].to_real (); } } return v; } + bool have_coords () const { return num_coords; } + protected: const int *coords; unsigned int num_coords; @@ -220,7 +224,10 @@ struct cff2_cs_opset_t : cs_opset_t, PAR const hb_array_t blends, unsigned n, unsigned i) { - arg.set_blends (n, i, blends.length, blends); + if (env.have_coords ()) + arg.set_int (round (arg.to_real () + env.blend_deltas (blends))); + else + arg.set_blends (n, i, blends); } template diff --git a/src/java.desktop/share/native/libharfbuzz/hb-common.cc b/src/java.desktop/share/native/libharfbuzz/hb-common.cc index 310053e89df..ef862618d11 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-common.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-common.cc @@ -29,32 +29,6 @@ #include "hb.hh" #include "hb-machinery.hh" -#if !defined(HB_NO_SETLOCALE) && (!defined(HAVE_NEWLOCALE) || !defined(HAVE_USELOCALE)) -#define HB_NO_SETLOCALE 1 -#endif - -#ifndef HB_NO_SETLOCALE - -#include -#ifdef HAVE_XLOCALE_H -#include // Needed on BSD/OS X for uselocale -#endif - -#ifdef WIN32 -#define hb_locale_t _locale_t -#else -#define hb_locale_t locale_t -#endif -#define hb_setlocale setlocale -#define hb_uselocale uselocale - -#else - -#define hb_locale_t void * -#define hb_setlocale(Category, Locale) "C" -#define hb_uselocale(Locale) ((hb_locale_t) 0) - -#endif /** * SECTION:hb-common @@ -99,7 +73,7 @@ _hb_options_init () } /* This is idempotent and threadsafe. */ - _hb_options.set_relaxed (u.i); + _hb_options = u.i; } @@ -108,7 +82,7 @@ _hb_options_init () /** * hb_tag_from_string: * @str: (array length=len) (element-type uint8_t): String to convert - * @len: Length of @str, or -1 if it is %NULL-terminated + * @len: Length of @str, or -1 if it is `NULL`-terminated * * Converts a string into an #hb_tag_t. Valid tags * are four characters. Shorter input strings will be @@ -170,7 +144,7 @@ static const char direction_strings[][4] = { /** * hb_direction_from_string: * @str: (array length=len) (element-type uint8_t): String to convert - * @len: Length of @str, or -1 if it is %NULL-terminated + * @len: Length of @str, or -1 if it is `NULL`-terminated * * Converts a string to an #hb_direction_t. * @@ -285,7 +259,7 @@ struct hb_language_item_t { lang = (hb_language_t) hb_malloc(len); if (likely (lang)) { - memcpy((unsigned char *) lang, s, len); + hb_memcpy((unsigned char *) lang, s, len); for (unsigned char *p = (unsigned char *) lang; *p; p++) *p = canon_map[*p]; } @@ -357,7 +331,7 @@ lang_find_or_insert (const char *key) * hb_language_from_string: * @str: (array length=len) (element-type uint8_t): a string representing * a BCP 47 language tag - * @len: length of the @str, or -1 if it is %NULL-terminated. + * @len: length of the @str, or -1 if it is `NULL`-terminated. * * Converts @str representing a BCP 47 language tag to the corresponding * #hb_language_t. @@ -379,7 +353,7 @@ hb_language_from_string (const char *str, int len) /* NUL-terminate it. */ char strbuf[64]; len = hb_min (len, (int) sizeof (strbuf) - 1); - memcpy (strbuf, str, len); + hb_memcpy (strbuf, str, len); strbuf[len] = '\0'; item = lang_find_or_insert (strbuf); } @@ -396,7 +370,7 @@ hb_language_from_string (const char *str, int len) * Converts an #hb_language_t to a string. * * Return value: (transfer none): - * A %NULL-terminated string representing the @language. Must not be freed by + * A `NULL`-terminated string representing the @language. Must not be freed by * the caller. * * Since: 0.9.2 @@ -441,6 +415,38 @@ hb_language_get_default () return language; } +/** + * hb_language_matches: + * @language: The #hb_language_t to work on + * @specific: Another #hb_language_t + * + * Check whether a second language tag is the same or a more + * specific version of the provided language tag. For example, + * "fa_IR.utf8" is a more specific tag for "fa" or for "fa_IR". + * + * Return value: `true` if languages match, `false` otherwise. + * + * Since: 5.0.0 + **/ +hb_bool_t +hb_language_matches (hb_language_t language, + hb_language_t specific) +{ + if (language == specific) return true; + if (!language || !specific) return false; + + const char *l = language->s; + const char *s = specific->s; + unsigned ll = strlen (l); + unsigned sl = strlen (s); + + if (ll > sl) + return false; + + return strncmp (l, s, ll) == 0 && + (s[ll] == '\0' || s[ll] == '-'); +} + /* hb_script_t */ @@ -498,7 +504,7 @@ hb_script_from_iso15924_tag (hb_tag_t tag) * hb_script_from_string: * @str: (array length=len) (element-type uint8_t): a string representing an * ISO 15924 tag. - * @len: length of the @str, or -1 if it is %NULL-terminated. + * @len: length of the @str, or -1 if it is `NULL`-terminated. * * Converts a string @str representing an ISO 15924 script tag to a * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then @@ -693,8 +699,8 @@ hb_version_string () * Tests the library version against a minimum value, * as three integer components. * - * Return value: %true if the library is equal to or greater than - * the test value, %false otherwise + * Return value: `true` if the library is equal to or greater than + * the test value, `false` otherwise * * Since: 0.9.30 **/ @@ -881,7 +887,7 @@ parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) /** * hb_feature_from_string: * @str: (array length=len) (element-type uint8_t): a string to parse - * @len: length of @str, or -1 if string is %NULL terminated + * @len: length of @str, or -1 if string is `NULL` terminated * @feature: (out): the #hb_feature_t to initialize with the parsed values * * Parses a string into a #hb_feature_t. @@ -923,7 +929,7 @@ parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) * * * Return value: - * %true if @str is successfully parsed, %false otherwise + * `true` if @str is successfully parsed, `false` otherwise * * Since: 0.9.5 **/ @@ -944,7 +950,7 @@ hb_feature_from_string (const char *str, int len, } if (feature) - memset (feature, 0, sizeof (*feature)); + hb_memset (feature, 0, sizeof (*feature)); return false; } @@ -954,7 +960,7 @@ hb_feature_from_string (const char *str, int len, * @buf: (array length=size) (out): output string * @size: the allocated size of @buf * - * Converts a #hb_feature_t into a %NULL-terminated string in the format + * Converts a #hb_feature_t into a `NULL`-terminated string in the format * understood by hb_feature_from_string(). The client in responsible for * allocating big enough size for @buf, 128 bytes is more than enough. * @@ -993,7 +999,7 @@ hb_feature_to_string (hb_feature_t *feature, } assert (len < ARRAY_LENGTH (s)); len = hb_min (len, size - 1); - memcpy (buf, s, len); + hb_memcpy (buf, s, len); buf[len] = '\0'; } @@ -1022,7 +1028,7 @@ parse_one_variation (const char **pp, const char *end, hb_variation_t *variation /** * hb_variation_from_string: * @str: (array length=len) (element-type uint8_t): a string to parse - * @len: length of @str, or -1 if string is %NULL terminated + * @len: length of @str, or -1 if string is `NULL` terminated * @variation: (out): the #hb_variation_t to initialize with the parsed values * * Parses a string into a #hb_variation_t. @@ -1035,7 +1041,7 @@ parse_one_variation (const char **pp, const char *end, hb_variation_t *variation * number. For example `wght=500`, or `slnt=-7.5`. * * Return value: - * %true if @str is successfully parsed, %false otherwise + * `true` if @str is successfully parsed, `false` otherwise * * Since: 1.4.2 */ @@ -1056,7 +1062,7 @@ hb_variation_from_string (const char *str, int len, } if (variation) - memset (variation, 0, sizeof (*variation)); + hb_memset (variation, 0, sizeof (*variation)); return false; } @@ -1104,10 +1110,10 @@ get_C_locale () /** * hb_variation_to_string: * @variation: an #hb_variation_t to convert - * @buf: (array length=size) (out): output string + * @buf: (array length=size) (out caller-allocates): output string * @size: the allocated size of @buf * - * Converts an #hb_variation_t into a %NULL-terminated string in the format + * Converts an #hb_variation_t into a `NULL`-terminated string in the format * understood by hb_variation_from_string(). The client in responsible for * allocating big enough size for @buf, 128 bytes is more than enough. * @@ -1134,7 +1140,7 @@ hb_variation_to_string (hb_variation_t *variation, assert (len < ARRAY_LENGTH (s)); len = hb_min (len, size - 1); - memcpy (buf, s, len); + hb_memcpy (buf, s, len); buf[len] = '\0'; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-common.h b/src/java.desktop/share/native/libharfbuzz/hb-common.h index 95daf0ed6a2..ebdeadd1fd1 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-common.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-common.h @@ -326,6 +326,9 @@ hb_language_to_string (hb_language_t language); HB_EXTERN hb_language_t hb_language_get_default (void); +HB_EXTERN hb_bool_t +hb_language_matches (hb_language_t language, + hb_language_t specific); /** * hb_script_t: @@ -492,6 +495,8 @@ hb_language_get_default (void); * @HB_SCRIPT_TOTO: `Toto`, Since: 3.0.0 * @HB_SCRIPT_VITHKUQI: `Vith`, Since: 3.0.0 * @HB_SCRIPT_MATH: `Zmth`, Since: 3.4.0 + * @HB_SCRIPT_KAWI: `Kawi`, Since: 5.2.0 + * @HB_SCRIPT_NAG_MUNDARI: `Nagm`, Since: 5.2.0 * @HB_SCRIPT_INVALID: No script set * * Data type for scripts. Each #hb_script_t's value is an #hb_tag_t corresponding @@ -713,6 +718,12 @@ typedef enum */ HB_SCRIPT_MATH = HB_TAG ('Z','m','t','h'), + /* + * Since 5.2.0 + */ + HB_SCRIPT_KAWI = HB_TAG ('K','a','w','i'), /*15.0*/ + HB_SCRIPT_NAG_MUNDARI = HB_TAG ('N','a','g','m'), /*15.0*/ + /* No script set. */ HB_SCRIPT_INVALID = HB_TAG_NONE, @@ -886,6 +897,32 @@ HB_EXTERN uint8_t hb_color_get_blue (hb_color_t color); #define hb_color_get_blue(color) (((color) >> 24) & 0xFF) +/** + * hb_glyph_extents_t: + * @x_bearing: Distance from the x-origin to the left extremum of the glyph. + * @y_bearing: Distance from the top extremum of the glyph to the y-origin. + * @width: Distance from the left extremum of the glyph to the right extremum. + * @height: Distance from the top extremum of the glyph to the bottom extremum. + * + * Glyph extent values, measured in font units. + * + * Note that @height is negative, in coordinate systems that grow up. + **/ +typedef struct hb_glyph_extents_t { + hb_position_t x_bearing; + hb_position_t y_bearing; + hb_position_t width; + hb_position_t height; +} hb_glyph_extents_t; + +/** + * hb_font_t: + * + * Data type for holding fonts. + * + */ +typedef struct hb_font_t hb_font_t; + HB_END_DECLS #endif /* HB_COMMON_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-config.hh b/src/java.desktop/share/native/libharfbuzz/hb-config.hh index 2578231d238..047518b87d4 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-config.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-config.hh @@ -35,6 +35,11 @@ #include "config.h" #endif +#ifndef HB_EXPERIMENTAL_API +#define HB_NO_BEYOND_64K +#define HB_NO_CUBIC_GLYF +#define HB_NO_VAR_COMPOSITES +#endif #ifdef HB_TINY #define HB_LEAN @@ -68,6 +73,7 @@ #define HB_NO_LANGUAGE_PRIVATE_SUBTAG #define HB_NO_LAYOUT_FEATURE_PARAMS #define HB_NO_LAYOUT_COLLECT_GLYPHS +#define HB_NO_LAYOUT_RARELY_USED #define HB_NO_LAYOUT_UNUSED #define HB_NO_MATH #define HB_NO_META @@ -75,11 +81,13 @@ #define HB_NO_MMAP #define HB_NO_NAME #define HB_NO_OPEN -#define HB_NO_SETLOCALE #define HB_NO_OT_FONT_GLYPH_NAMES #define HB_NO_OT_SHAPE_FRACTIONS +#define HB_NO_PAINT +#define HB_NO_SETLOCALE #define HB_NO_STYLE #define HB_NO_SUBSET_LAYOUT +#define HB_NO_VERTICAL #define HB_NO_VAR #endif @@ -98,12 +106,22 @@ /* Closure of options. */ +#ifdef HB_NO_BORING_EXPANSION +#define HB_NO_BEYOND_64K +#define HB_NO_AVAR2 +#endif + #ifdef HB_DISABLE_DEPRECATED #define HB_IF_NOT_DEPRECATED(x) #else #define HB_IF_NOT_DEPRECATED(x) x #endif +#ifdef HB_NO_SHAPER +#define HB_NO_OT_SHAPE +#define HB_NO_AAT_SHAPE +#endif + #ifdef HB_NO_AAT #define HB_NO_OT_NAME_LANGUAGE_AAT #define HB_NO_AAT_SHAPE @@ -150,6 +168,7 @@ #define HB_NO_OT_SHAPER_HEBREW_FALLBACK #define HB_NO_OT_SHAPER_THAI_FALLBACK #define HB_NO_OT_SHAPER_VOWEL_CONSTRAINTS +#define HB_NO_OT_SHAPER_MYANMAR_ZAWGYI #endif #ifdef NDEBUG diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cplusplus.hh b/src/java.desktop/share/native/libharfbuzz/hb-cplusplus.hh index adfbc3c11bf..eac4ff03ed5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cplusplus.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cplusplus.hh @@ -69,9 +69,9 @@ struct shared_ptr operator T * () const { return p; } T& operator * () const { return *get (); } T* operator -> () const { return get (); } - operator bool () { return p; } - bool operator == (const shared_ptr &o) { return p == o.p; } - bool operator != (const shared_ptr &o) { return p != o.p; } + operator bool () const { return p; } + bool operator == (const shared_ptr &o) const { return p == o.p; } + bool operator != (const shared_ptr &o) const { return p != o.p; } static T* get_empty() { return v::get_empty (); } T* reference() { return v::reference (p); } @@ -130,7 +130,7 @@ template struct vtable_t { @@ -160,14 +160,43 @@ HB_DEFINE_VTABLE (map); HB_DEFINE_VTABLE (set); HB_DEFINE_VTABLE (shape_plan); HB_DEFINE_VTABLE (unicode_funcs); +HB_DEFINE_VTABLE (draw_funcs); +HB_DEFINE_VTABLE (paint_funcs); #undef HB_DEFINE_VTABLE +#ifdef HB_SUBSET_H + +#define HB_DEFINE_VTABLE(name) \ + template<> \ + struct vtable \ + : vtable_t {} + + +HB_DEFINE_VTABLE (subset_input); +HB_DEFINE_VTABLE (subset_plan); + +#undef HB_DEFINE_VTABLE + +#endif + + } // namespace hb +/* Workaround for GCC < 7, see: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 + * https://stackoverflow.com/a/25594741 */ +namespace std { + + template -struct std::hash> +struct hash> { std::size_t operator()(const hb::shared_ptr& v) const noexcept { @@ -177,7 +206,7 @@ struct std::hash> }; template -struct std::hash> +struct hash> { std::size_t operator()(const hb::unique_ptr& v) const noexcept { @@ -187,6 +216,8 @@ struct std::hash> }; +} // namespace std + #endif /* __cplusplus */ #endif /* HB_CPLUSPLUS_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh index 09c407e415d..43647c259f6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh @@ -67,12 +67,12 @@ hb_options () #endif /* Make a local copy, so we can access bitfield threadsafely. */ hb_options_union_t u; - u.i = _hb_options.get_relaxed (); + u.i = _hb_options; if (unlikely (!u.i)) { _hb_options_init (); - u.i = _hb_options.get_relaxed (); + u.i = _hb_options; } return u.opts; @@ -113,7 +113,7 @@ _hb_print_func (const char *func) const char *paren = strchr (func, '('); if (paren) func_len = paren - func; - fprintf (stderr, "%.*s", func_len, func); + fprintf (stderr, "%.*s", (int) func_len, func); } } @@ -142,9 +142,9 @@ _hb_debug_msg_va (const char *what, fprintf (stderr, "%-10s", what ? what : ""); if (obj) - fprintf (stderr, "(%*p) ", (unsigned int) (2 * sizeof (void *)), obj); + fprintf (stderr, "(%*p) ", (int) (2 * sizeof (void *)), obj); else - fprintf (stderr, " %*s ", (unsigned int) (2 * sizeof (void *)), ""); + fprintf (stderr, " %*s ", (int) (2 * sizeof (void *)), ""); if (indented) { #define VBAR "\342\224\202" /* U+2502 BOX DRAWINGS LIGHT VERTICAL */ @@ -306,7 +306,7 @@ struct hb_auto_trace_t } _hb_debug_msg (what, obj, func, true, plevel ? *plevel : 1, -1, - "return %s (line %d)", + "return %s (line %u)", hb_printer_t>().print (v), line); if (plevel) --*plevel; plevel = nullptr; @@ -396,7 +396,7 @@ struct hb_no_trace_t { #define TRACE_APPLY(this) \ hb_auto_trace_t trace \ (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - "idx %d gid %u lookup %d", \ + "idx %u gid %u lookup %d", \ c->buffer->idx, c->buffer->cur().codepoint, (int) c->lookup_index) #else #define TRACE_APPLY(this) hb_no_trace_t trace @@ -454,10 +454,15 @@ struct hb_no_trace_t { #define TRACE_DISPATCH(this, format) \ hb_auto_trace_t trace \ (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - "format %d", (int) format) + "format %u", (unsigned) format) #else #define TRACE_DISPATCH(this, format) hb_no_trace_t trace #endif +#ifndef HB_BUFFER_MESSAGE_MORE +#define HB_BUFFER_MESSAGE_MORE (HB_DEBUG+1) +#endif + + #endif /* HB_DEBUG_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h index 55f1e1205df..c6b67291878 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h @@ -93,7 +93,7 @@ HB_BEGIN_DECLS * This method should retrieve the glyph ID for a specified Unicode code point * font, with an optional variation selector. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * Deprecated: 1.2.3 * **/ @@ -102,7 +102,8 @@ typedef hb_bool_t (*hb_font_get_glyph_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t *glyph, void *user_data); -HB_EXTERN HB_DEPRECATED_FOR(hb_font_funcs_set_nominal_glyph_func and hb_font_funcs_set_variation_glyph_func) void +HB_DEPRECATED_FOR (hb_font_funcs_set_nominal_glyph_func and hb_font_funcs_set_variation_glyph_func) +HB_EXTERN void hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_func_t func, void *user_data, hb_destroy_func_t destroy); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.cc b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc index 6a9ab012409..f2821578b65 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-draw.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc @@ -35,6 +35,8 @@ * @include: hb.h * * Functions for drawing (extracting) glyph shapes. + * + * The #hb_draw_funcs_t struct can be used with hb_font_draw_glyph(). **/ static void @@ -80,6 +82,56 @@ hb_draw_close_path_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UN void *user_data HB_UNUSED) {} +static bool +_hb_draw_funcs_set_preamble (hb_draw_funcs_t *dfuncs, + bool func_is_null, + void **user_data, + hb_destroy_func_t *destroy) +{ + if (hb_object_is_immutable (dfuncs)) + { + if (*destroy) + (*destroy) (*user_data); + return false; + } + + if (func_is_null) + { + if (*destroy) + (*destroy) (*user_data); + *destroy = nullptr; + *user_data = nullptr; + } + + return true; +} + +static bool +_hb_draw_funcs_set_middle (hb_draw_funcs_t *dfuncs, + void *user_data, + hb_destroy_func_t destroy) +{ + if (user_data && !dfuncs->user_data) + { + dfuncs->user_data = (decltype (dfuncs->user_data)) hb_calloc (1, sizeof (*dfuncs->user_data)); + if (unlikely (!dfuncs->user_data)) + goto fail; + } + if (destroy && !dfuncs->destroy) + { + dfuncs->destroy = (decltype (dfuncs->destroy)) hb_calloc (1, sizeof (*dfuncs->destroy)); + if (unlikely (!dfuncs->destroy)) + goto fail; + } + + return true; + +fail: + if (destroy) + (destroy) (user_data); + return false; +} + #define HB_DRAW_FUNC_IMPLEMENT(name) \ \ void \ @@ -88,42 +140,24 @@ hb_draw_funcs_set_##name##_func (hb_draw_funcs_t *dfuncs, void *user_data, \ hb_destroy_func_t destroy) \ { \ - if (hb_object_is_immutable (dfuncs)) \ - return; \ + if (!_hb_draw_funcs_set_preamble (dfuncs, !func, &user_data, &destroy))\ + return; \ \ if (dfuncs->destroy && dfuncs->destroy->name) \ dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name); \ \ - if (user_data && !dfuncs->user_data) \ - { \ - dfuncs->user_data = (decltype (dfuncs->user_data)) hb_calloc (1, sizeof (*dfuncs->user_data)); \ - if (unlikely (!dfuncs->user_data)) \ - goto fail; \ - } \ - if (destroy && !dfuncs->destroy) \ - { \ - dfuncs->destroy = (decltype (dfuncs->destroy)) hb_calloc (1, sizeof (*dfuncs->destroy)); \ - if (unlikely (!dfuncs->destroy)) \ - goto fail; \ - } \ + if (!_hb_draw_funcs_set_middle (dfuncs, user_data, destroy)) \ + return; \ \ - if (func) { \ + if (func) \ dfuncs->func.name = func; \ - if (dfuncs->user_data) \ - dfuncs->user_data->name = user_data; \ - if (dfuncs->destroy) \ - dfuncs->destroy->name = destroy; \ - } else { \ + else \ dfuncs->func.name = hb_draw_##name##_nil; \ - if (dfuncs->user_data) \ - dfuncs->user_data->name = nullptr; \ - if (dfuncs->destroy) \ - dfuncs->destroy->name = nullptr; \ - } \ - \ -fail: \ - if (destroy) \ - destroy (user_data); \ + \ + if (dfuncs->user_data) \ + dfuncs->user_data->name = user_data; \ + if (dfuncs->destroy) \ + dfuncs->destroy->name = destroy; \ } HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS @@ -137,7 +171,7 @@ HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS * Return value: (transfer full): * A newly allocated #hb_draw_funcs_t with a reference count of 1. The initial * reference count should be released with hb_draw_funcs_destroy when you are - * done using the #hb_draw_funcs_t. This function never returns %NULL. If + * done using the #hb_draw_funcs_t. This function never returns `NULL`. If * memory cannot be allocated, a special singleton #hb_draw_funcs_t object will * be returned. * @@ -166,13 +200,29 @@ DEFINE_NULL_INSTANCE (hb_draw_funcs_t) = } }; +/** + * hb_draw_funcs_get_empty: + * + * Fetches the singleton empty draw-functions structure. + * + * Return value: (transfer full): The empty draw-functions structure + * + * Since: 7.0.0 + **/ +hb_draw_funcs_t * +hb_draw_funcs_get_empty () +{ + return const_cast (&Null (hb_draw_funcs_t)); +} /** * hb_draw_funcs_reference: (skip) * @dfuncs: draw functions * - * Increases the reference count on @dfuncs by one. This prevents @buffer from - * being destroyed until a matching call to hb_draw_funcs_destroy() is made. + * Increases the reference count on @dfuncs by one. + * + * This prevents @dfuncs from being destroyed until a matching + * call to hb_draw_funcs_destroy() is made. * * Return value: (transfer full): * The referenced #hb_draw_funcs_t. @@ -208,9 +258,55 @@ hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs) #undef HB_DRAW_FUNC_IMPLEMENT } + hb_free (dfuncs->destroy); + hb_free (dfuncs->user_data); + hb_free (dfuncs); } +/** + * hb_draw_funcs_set_user_data: (skip) + * @dfuncs: The draw-functions structure + * @key: The user-data key + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified draw-functions structure. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 7.0.0 + **/ +hb_bool_t +hb_draw_funcs_set_user_data (hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (dfuncs, key, data, destroy, replace); +} + +/** + * hb_draw_funcs_get_user_data: (skip) + * @dfuncs: The draw-functions structure + * @key: The user-data key to query + * + * Fetches the user-data associated with the specified key, + * attached to the specified draw-functions structure. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 7.0.0 + **/ +void * +hb_draw_funcs_get_user_data (const hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (dfuncs, key); +} + /** * hb_draw_funcs_make_immutable: * @dfuncs: draw functions @@ -234,7 +330,7 @@ hb_draw_funcs_make_immutable (hb_draw_funcs_t *dfuncs) * * Checks whether @dfuncs is immutable. * - * Return value: %true if @dfuncs is immutable, %false otherwise + * Return value: `true` if @dfuncs is immutable, `false` otherwise * * Since: 4.0.0 **/ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.h b/src/java.desktop/share/native/libharfbuzz/hb-draw.h index 364db5f7f56..eba791d10c0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-draw.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.h @@ -92,11 +92,11 @@ typedef struct hb_draw_funcs_t hb_draw_funcs_t; /** * hb_draw_move_to_func_t: * @dfuncs: draw functions object - * @draw_data: The data accompanying the draw functions + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() * @st: current draw state * @to_x: X component of target point * @to_y: Y component of target point - * @user_data: User data pointer passed by the caller + * @user_data: User data pointer passed to hb_draw_funcs_set_move_to_func() * * A virtual method for the #hb_draw_funcs_t to perform a "move-to" draw * operation. @@ -112,11 +112,11 @@ typedef void (*hb_draw_move_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data /** * hb_draw_line_to_func_t: * @dfuncs: draw functions object - * @draw_data: The data accompanying the draw functions + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() * @st: current draw state * @to_x: X component of target point * @to_y: Y component of target point - * @user_data: User data pointer passed by the caller + * @user_data: User data pointer passed to hb_draw_funcs_set_line_to_func() * * A virtual method for the #hb_draw_funcs_t to perform a "line-to" draw * operation. @@ -132,13 +132,13 @@ typedef void (*hb_draw_line_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data /** * hb_draw_quadratic_to_func_t: * @dfuncs: draw functions object - * @draw_data: The data accompanying the draw functions + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() * @st: current draw state * @control_x: X component of control point * @control_y: Y component of control point * @to_x: X component of target point * @to_y: Y component of target point - * @user_data: User data pointer passed by the caller + * @user_data: User data pointer passed to hb_draw_funcs_set_quadratic_to_func() * * A virtual method for the #hb_draw_funcs_t to perform a "quadratic-to" draw * operation. @@ -155,7 +155,7 @@ typedef void (*hb_draw_quadratic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw /** * hb_draw_cubic_to_func_t: * @dfuncs: draw functions object - * @draw_data: The data accompanying the draw functions + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() * @st: current draw state * @control1_x: X component of first control point * @control1_y: Y component of first control point @@ -163,7 +163,7 @@ typedef void (*hb_draw_quadratic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw * @control2_y: Y component of second control point * @to_x: X component of target point * @to_y: Y component of target point - * @user_data: User data pointer passed by the caller + * @user_data: User data pointer passed to hb_draw_funcs_set_cubic_to_func() * * A virtual method for the #hb_draw_funcs_t to perform a "cubic-to" draw * operation. @@ -181,9 +181,9 @@ typedef void (*hb_draw_cubic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_dat /** * hb_draw_close_path_func_t: * @dfuncs: draw functions object - * @draw_data: The data accompanying the draw functions + * @draw_data: The data accompanying the draw functions in hb_font_draw_glyph() * @st: current draw state - * @user_data: User data pointer passed by the caller + * @user_data: User data pointer passed to hb_draw_funcs_set_close_path_func() * * A virtual method for the #hb_draw_funcs_t to perform a "close-path" draw * operation. @@ -279,12 +279,27 @@ hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *dfuncs, HB_EXTERN hb_draw_funcs_t * hb_draw_funcs_create (void); +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_get_empty (void); + HB_EXTERN hb_draw_funcs_t * hb_draw_funcs_reference (hb_draw_funcs_t *dfuncs); HB_EXTERN void hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs); +HB_EXTERN hb_bool_t +hb_draw_funcs_set_user_data (hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +HB_EXTERN void * +hb_draw_funcs_get_user_data (const hb_draw_funcs_t *dfuncs, + hb_user_data_key_t *key); + HB_EXTERN void hb_draw_funcs_make_immutable (hb_draw_funcs_t *dfuncs); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc b/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc new file mode 100644 index 00000000000..ff723ac7000 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc @@ -0,0 +1,246 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2012 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-face.hh" + +#include "hb-map.hh" +#include "hb-open-file.hh" +#include "hb-serialize.hh" + + +/* + * face-builder: A face that has add_table(). + */ + +struct face_table_info_t +{ + hb_blob_t* data; + signed order; +}; + +struct hb_face_builder_data_t +{ + hb_hashmap_t tables; +}; + +static int compare_entries (const void* pa, const void* pb) +{ + const auto& a = * (const hb_pair_t *) pa; + const auto& b = * (const hb_pair_t *) pb; + + /* Order by blob size first (smallest to largest) and then table tag */ + + if (a.second.order != b.second.order) + return a.second.order < b.second.order ? -1 : +1; + + if (a.second.data->length != b.second.data->length) + return a.second.data->length < b.second.data->length ? -1 : +1; + + return a.first < b.first ? -1 : a.first == b.first ? 0 : +1; +} + +static hb_face_builder_data_t * +_hb_face_builder_data_create () +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) hb_calloc (1, sizeof (hb_face_builder_data_t)); + if (unlikely (!data)) + return nullptr; + + data->tables.init (); + + return data; +} + +static void +_hb_face_builder_data_destroy (void *user_data) +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; + + for (auto info : data->tables.values()) + hb_blob_destroy (info.data); + + data->tables.fini (); + + hb_free (data); +} + +static hb_blob_t * +_hb_face_builder_data_reference_blob (hb_face_builder_data_t *data) +{ + + unsigned int table_count = data->tables.get_population (); + unsigned int face_length = table_count * 16 + 12; + + for (auto info : data->tables.values()) + face_length += hb_ceil_to_4 (hb_blob_get_length (info.data)); + + char *buf = (char *) hb_malloc (face_length); + if (unlikely (!buf)) + return nullptr; + + hb_serialize_context_t c (buf, face_length); + c.propagate_error (data->tables); + OT::OpenTypeFontFile *f = c.start_serialize (); + + bool is_cff = (data->tables.has (HB_TAG ('C','F','F',' ')) + || data->tables.has (HB_TAG ('C','F','F','2'))); + hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag; + + // Sort the tags so that produced face is deterministic. + hb_vector_t> sorted_entries; + data->tables.iter () | hb_sink (sorted_entries); + if (unlikely (sorted_entries.in_error ())) + { + hb_free (buf); + return nullptr; + } + + sorted_entries.qsort (compare_entries); + + bool ret = f->serialize_single (&c, + sfnt_tag, + + sorted_entries.iter() + | hb_map ([&] (hb_pair_t _) { + return hb_pair_t (_.first, _.second.data); + })); + + c.end_serialize (); + + if (unlikely (!ret)) + { + hb_free (buf); + return nullptr; + } + + return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, hb_free); +} + +static hb_blob_t * +_hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; + + if (!tag) + return _hb_face_builder_data_reference_blob (data); + + return hb_blob_reference (data->tables[tag].data); +} + + +/** + * hb_face_builder_create: + * + * Creates a #hb_face_t that can be used with hb_face_builder_add_table(). + * After tables are added to the face, it can be compiled to a binary + * font file by calling hb_face_reference_blob(). + * + * Return value: (transfer full): New face. + * + * Since: 1.9.0 + **/ +hb_face_t * +hb_face_builder_create () +{ + hb_face_builder_data_t *data = _hb_face_builder_data_create (); + if (unlikely (!data)) return hb_face_get_empty (); + + return hb_face_create_for_tables (_hb_face_builder_reference_table, + data, + _hb_face_builder_data_destroy); +} + +/** + * hb_face_builder_add_table: + * @face: A face object created with hb_face_builder_create() + * @tag: The #hb_tag_t of the table to add + * @blob: The blob containing the table data to add + * + * Add table for @tag with data provided by @blob to the face. @face must + * be created using hb_face_builder_create(). + * + * Since: 1.9.0 + **/ +hb_bool_t +hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) +{ + if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy)) + return false; + + if (tag == HB_MAP_VALUE_INVALID) + return false; + + hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; + + hb_blob_t* previous = data->tables.get (tag).data; + if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), -1})) + { + hb_blob_destroy (blob); + return false; + } + + hb_blob_destroy (previous); + return true; +} + +/** + * hb_face_builder_sort_tables: + * @face: A face object created with hb_face_builder_create() + * @tags: (array zero-terminated=1): ordered list of table tags terminated by + * %HB_TAG_NONE + * + * Set the ordering of tables for serialization. Any tables not + * specified in the tags list will be ordered after the tables in + * tags, ordered by the default sort ordering. + * + * Since: 5.3.0 + **/ +void +hb_face_builder_sort_tables (hb_face_t *face, + const hb_tag_t *tags) +{ + if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy)) + return; + + hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; + + // Sort all unspecified tables after any specified tables. + for (auto& info : data->tables.values_ref()) + info.order = (unsigned) -1; + + signed order = 0; + for (const hb_tag_t* tag = tags; + *tag; + tag++) + { + face_table_info_t* info; + if (!data->tables.has (*tag, &info)) continue; + info->order = order++; + } +} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.cc b/src/java.desktop/share/native/libharfbuzz/hb-face.cc index 43a32484701..8bb9f16af94 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-face.cc @@ -33,7 +33,6 @@ #include "hb-open-file.hh" #include "hb-ot-face.hh" #include "hb-ot-cmap-table.hh" -#include "hb-map.hh" /** @@ -132,7 +131,7 @@ hb_face_create_for_tables (hb_reference_table_func_t reference_table_func, face->user_data = user_data; face->destroy = destroy; - face->num_glyphs.set_relaxed (-1); + face->num_glyphs = -1; face->data.init0 (face); face->table.init0 (face); @@ -288,6 +287,7 @@ hb_face_destroy (hb_face_t *face) { if (!hb_object_destroy (face)) return; +#ifndef HB_NO_SHAPER for (hb_face_t::plan_node_t *node = face->shape_plans; node; ) { hb_face_t::plan_node_t *next = node->next; @@ -295,6 +295,7 @@ hb_face_destroy (hb_face_t *face) hb_free (node); node = next; } +#endif face->data.fini (); face->table.fini (); @@ -315,7 +316,7 @@ hb_face_destroy (hb_face_t *face) * * Attaches a user-data key/data pair to the given face object. * - * Return value: %true if success, %false otherwise + * Return value: `true` if success, `false` otherwise * * Since: 0.9.2 **/ @@ -342,7 +343,7 @@ hb_face_set_user_data (hb_face_t *face, * Since: 0.9.2 **/ void * -hb_face_get_user_data (hb_face_t *face, +hb_face_get_user_data (const hb_face_t *face, hb_user_data_key_t *key) { return hb_object_get_user_data (face, key); @@ -371,7 +372,7 @@ hb_face_make_immutable (hb_face_t *face) * * Tests whether the given face object is immutable. * - * Return value: %true is @face is immutable, %false otherwise + * Return value: `true` is @face is immutable, `false` otherwise * * Since: 0.9.2 **/ @@ -470,6 +471,8 @@ hb_face_get_index (const hb_face_t *face) * * Sets the units-per-em (upem) for a face object to the specified value. * + * This API is used in rare circumstances. + * * Since: 0.9.2 **/ void @@ -479,14 +482,17 @@ hb_face_set_upem (hb_face_t *face, if (hb_object_is_immutable (face)) return; - face->upem.set_relaxed (upem); + face->upem = upem; } /** * hb_face_get_upem: * @face: A face object * - * Fetches the units-per-em (upem) value of the specified face object. + * Fetches the units-per-em (UPEM) value of the specified face object. + * + * Typical UPEM values for fonts are 1000, or 2048, but any value + * in between 16 and 16,384 is allowed for OpenType fonts. * * Return value: The upem value of @face * @@ -505,6 +511,8 @@ hb_face_get_upem (const hb_face_t *face) * * Sets the glyph count for a face object to the specified value. * + * This API is used in rare circumstances. + * * Since: 0.9.7 **/ void @@ -514,7 +522,7 @@ hb_face_set_glyph_count (hb_face_t *face, if (hb_object_is_immutable (face)) return; - face->num_glyphs.set_relaxed (glyph_count); + face->num_glyphs = glyph_count; } /** @@ -579,7 +587,7 @@ hb_face_get_table_tags (const hb_face_t *face, /** * hb_face_collect_unicodes: * @face: A face object - * @out: The set to add Unicode characters to + * @out: (out): The set to add Unicode characters to * * Collects all of the Unicode characters covered by @face and adds * them to the #hb_set_t set @out. @@ -592,10 +600,31 @@ hb_face_collect_unicodes (hb_face_t *face, { face->table.cmap->collect_unicodes (out, face->get_num_glyphs ()); } +/** + * hb_face_collect_nominal_glyph_mapping: + * @face: A face object + * @mapping: (out): The map to add Unicode-to-glyph mapping to + * @unicodes: (nullable) (out): The set to add Unicode characters to, or `NULL` + * + * Collects the mapping from Unicode characters to nominal glyphs of the @face, + * and optionally all of the Unicode characters covered by @face. + * + * Since: 7.0.0 + */ +void +hb_face_collect_nominal_glyph_mapping (hb_face_t *face, + hb_map_t *mapping, + hb_set_t *unicodes) +{ + hb_set_t stack_unicodes; + if (!unicodes) + unicodes = &stack_unicodes; + face->table.cmap->collect_mapping (unicodes, mapping, face->get_num_glyphs ()); +} /** * hb_face_collect_variation_selectors: * @face: A face object - * @out: The set to add Variation Selector characters to + * @out: (out): The set to add Variation Selector characters to * * Collects all Unicode "Variation Selector" characters covered by @face and adds * them to the #hb_set_t set @out. @@ -612,7 +641,7 @@ hb_face_collect_variation_selectors (hb_face_t *face, * hb_face_collect_variation_unicodes: * @face: A face object * @variation_selector: The Variation Selector to query - * @out: The set to add Unicode characters to + * @out: (out): The set to add Unicode characters to * * Collects all Unicode characters for @variation_selector covered by @face and adds * them to the #hb_set_t set @out. @@ -627,163 +656,3 @@ hb_face_collect_variation_unicodes (hb_face_t *face, face->table.cmap->collect_variation_unicodes (variation_selector, out); } #endif - - -/* - * face-builder: A face that has add_table(). - */ - -struct hb_face_builder_data_t -{ - hb_hashmap_t tables; -}; - -static int compare_entries (const void* pa, const void* pb) -{ - const auto& a = * (const hb_pair_t *) pa; - const auto& b = * (const hb_pair_t *) pb; - - /* Order by blob size first (smallest to largest) and then table tag */ - - if (a.second->length != b.second->length) - return a.second->length < b.second->length ? -1 : +1; - - return a.first < b.first ? -1 : a.first == b.first ? 0 : +1; -} - -static hb_face_builder_data_t * -_hb_face_builder_data_create () -{ - hb_face_builder_data_t *data = (hb_face_builder_data_t *) hb_calloc (1, sizeof (hb_face_builder_data_t)); - if (unlikely (!data)) - return nullptr; - - data->tables.init (); - - return data; -} - -static void -_hb_face_builder_data_destroy (void *user_data) -{ - hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; - - for (hb_blob_t* b : data->tables.values()) - hb_blob_destroy (b); - - data->tables.fini (); - - hb_free (data); -} - -static hb_blob_t * -_hb_face_builder_data_reference_blob (hb_face_builder_data_t *data) -{ - - unsigned int table_count = data->tables.get_population (); - unsigned int face_length = table_count * 16 + 12; - - for (hb_blob_t* b : data->tables.values()) - face_length += hb_ceil_to_4 (hb_blob_get_length (b)); - - char *buf = (char *) hb_malloc (face_length); - if (unlikely (!buf)) - return nullptr; - - hb_serialize_context_t c (buf, face_length); - c.propagate_error (data->tables); - OT::OpenTypeFontFile *f = c.start_serialize (); - - bool is_cff = (data->tables.has (HB_TAG ('C','F','F',' ')) - || data->tables.has (HB_TAG ('C','F','F','2'))); - hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag; - - // Sort the tags so that produced face is deterministic. - hb_vector_t> sorted_entries; - data->tables.iter () | hb_sink (sorted_entries); - if (unlikely (sorted_entries.in_error ())) - { - hb_free (buf); - return nullptr; - } - - sorted_entries.qsort (compare_entries); - bool ret = f->serialize_single (&c, sfnt_tag, + sorted_entries.iter()); - - c.end_serialize (); - - if (unlikely (!ret)) - { - hb_free (buf); - return nullptr; - } - - return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, hb_free); -} - -static hb_blob_t * -_hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) -{ - hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; - - if (!tag) - return _hb_face_builder_data_reference_blob (data); - - return hb_blob_reference (data->tables[tag]); -} - - -/** - * hb_face_builder_create: - * - * Creates a #hb_face_t that can be used with hb_face_builder_add_table(). - * After tables are added to the face, it can be compiled to a binary - * font file by calling hb_face_reference_blob(). - * - * Return value: (transfer full): New face. - * - * Since: 1.9.0 - **/ -hb_face_t * -hb_face_builder_create () -{ - hb_face_builder_data_t *data = _hb_face_builder_data_create (); - if (unlikely (!data)) return hb_face_get_empty (); - - return hb_face_create_for_tables (_hb_face_builder_reference_table, - data, - _hb_face_builder_data_destroy); -} - -/** - * hb_face_builder_add_table: - * @face: A face object created with hb_face_builder_create() - * @tag: The #hb_tag_t of the table to add - * @blob: The blob containing the table data to add - * - * Add table for @tag with data provided by @blob to the face. @face must - * be created using hb_face_builder_create(). - * - * Since: 1.9.0 - **/ -hb_bool_t -hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) -{ - if (tag == HB_MAP_VALUE_INVALID) - return false; - - if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy)) - return false; - - hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; - - hb_blob_t* previous = data->tables.get (tag); - if (!data->tables.set (tag, hb_blob_reference (blob))) - { - hb_blob_destroy (blob); - return false; - } - - hb_blob_destroy (previous); - return true; -} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.h b/src/java.desktop/share/native/libharfbuzz/hb-face.h index e74db2933b9..2e5fb150927 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-face.h @@ -33,6 +33,7 @@ #include "hb-common.h" #include "hb-blob.h" +#include "hb-map.h" #include "hb-set.h" HB_BEGIN_DECLS @@ -96,7 +97,7 @@ hb_face_set_user_data (hb_face_t *face, hb_bool_t replace); HB_EXTERN void * -hb_face_get_user_data (hb_face_t *face, +hb_face_get_user_data (const hb_face_t *face, hb_user_data_key_t *key); HB_EXTERN void @@ -149,6 +150,11 @@ HB_EXTERN void hb_face_collect_unicodes (hb_face_t *face, hb_set_t *out); +HB_EXTERN void +hb_face_collect_nominal_glyph_mapping (hb_face_t *face, + hb_map_t *mapping, + hb_set_t *unicodes); + HB_EXTERN void hb_face_collect_variation_selectors (hb_face_t *face, hb_set_t *out); @@ -171,6 +177,10 @@ hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob); +HB_EXTERN void +hb_face_builder_sort_tables (hb_face_t *face, + const hb_tag_t *tags); + HB_END_DECLS diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.hh b/src/java.desktop/share/native/libharfbuzz/hb-face.hh index b4a74e9580f..738f7ecdc72 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-face.hh @@ -65,7 +65,9 @@ struct hb_face_t hb_shape_plan_t *shape_plan; plan_node_t *next; }; +#ifndef HB_NO_SHAPER hb_atomic_ptr_t shape_plans; +#endif hb_blob_t *reference_table (hb_tag_t tag) const { @@ -83,7 +85,7 @@ struct hb_face_t unsigned int get_upem () const { - unsigned int ret = upem.get_relaxed (); + unsigned int ret = upem; if (unlikely (!ret)) { return load_upem (); @@ -93,7 +95,7 @@ struct hb_face_t unsigned int get_num_glyphs () const { - unsigned int ret = num_glyphs.get_relaxed (); + unsigned int ret = num_glyphs; if (unlikely (ret == UINT_MAX)) return load_num_glyphs (); return ret; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc index cd1eaf5e21f..e37f90111eb 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc @@ -75,16 +75,6 @@ _hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED, const hb_feature_t *features HB_UNUSED, unsigned int num_features HB_UNUSED) { - /* TODO - * - * - Apply fallback kern. - * - Handle Variation Selectors? - * - Apply normalization? - * - * This will make the fallback shaper into a dumb "TrueType" - * shaper which many people unfortunately still request. - */ - hb_codepoint_t space; bool has_space = (bool) font->get_nominal_glyph (' ', &space); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.cc b/src/java.desktop/share/native/libharfbuzz/hb-font.cc index b36b53613f8..d118deb9285 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.cc @@ -30,6 +30,7 @@ #include "hb-font.hh" #include "hb-draw.hh" +#include "hb-paint.hh" #include "hb-machinery.hh" #include "hb-ot.h" @@ -71,7 +72,7 @@ hb_font_get_font_h_extents_nil (hb_font_t *font HB_UNUSED, hb_font_extents_t *extents, void *user_data HB_UNUSED) { - memset (extents, 0, sizeof (*extents)); + hb_memset (extents, 0, sizeof (*extents)); return false; } @@ -96,7 +97,7 @@ hb_font_get_font_v_extents_nil (hb_font_t *font HB_UNUSED, hb_font_extents_t *extents, void *user_data HB_UNUSED) { - memset (extents, 0, sizeof (*extents)); + hb_memset (extents, 0, sizeof (*extents)); return false; } @@ -409,7 +410,7 @@ hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED, hb_glyph_extents_t *extents, void *user_data HB_UNUSED) { - memset (extents, 0, sizeof (*extents)); + hb_memset (extents, 0, sizeof (*extents)); return false; } @@ -503,22 +504,34 @@ hb_font_get_glyph_from_name_default (hb_font_t *font, } static void -hb_font_get_glyph_shape_nil (hb_font_t *font HB_UNUSED, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, - void *draw_data, - void *user_data HB_UNUSED) +hb_font_draw_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, + void *draw_data, + void *user_data HB_UNUSED) { } +static void +hb_font_paint_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_paint_funcs_t *paint_funcs HB_UNUSED, + void *paint_data HB_UNUSED, + unsigned int palette HB_UNUSED, + hb_color_t foreground HB_UNUSED, + void *user_data HB_UNUSED) +{ +} -typedef struct hb_font_get_glyph_shape_default_adaptor_t { +typedef struct hb_font_draw_glyph_default_adaptor_t { hb_draw_funcs_t *draw_funcs; void *draw_data; float x_scale; float y_scale; -} hb_font_get_glyph_shape_default_adaptor_t; + float slant; +} hb_font_draw_glyph_default_adaptor_t; static void hb_draw_move_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, @@ -527,12 +540,13 @@ hb_draw_move_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, float to_x, float to_y, void *user_data HB_UNUSED) { - hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; + float slant = adaptor->slant; adaptor->draw_funcs->emit_move_to (adaptor->draw_data, *st, - x_scale * to_x, y_scale * to_y); + x_scale * to_x + slant * to_y, y_scale * to_y); } static void @@ -541,15 +555,16 @@ hb_draw_line_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, float to_x, float to_y, void *user_data HB_UNUSED) { - hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; + float slant = adaptor->slant; - st->current_x *= x_scale; - st->current_y *= y_scale; + st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_y = st->current_y * y_scale; adaptor->draw_funcs->emit_line_to (adaptor->draw_data, *st, - x_scale * to_x, y_scale * to_y); + x_scale * to_x + slant * to_y, y_scale * to_y); } static void @@ -559,16 +574,17 @@ hb_draw_quadratic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data float to_x, float to_y, void *user_data HB_UNUSED) { - hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; + float slant = adaptor->slant; - st->current_x *= x_scale; - st->current_y *= y_scale; + st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_y = st->current_y * y_scale; adaptor->draw_funcs->emit_quadratic_to (adaptor->draw_data, *st, - x_scale * control_x, y_scale * control_y, - x_scale * to_x, y_scale * to_y); + x_scale * control_x + slant * control_y, y_scale * control_y, + x_scale * to_x + slant * to_y, y_scale * to_y); } static void @@ -579,17 +595,18 @@ hb_draw_cubic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, float to_x, float to_y, void *user_data HB_UNUSED) { - hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; float x_scale = adaptor->x_scale; float y_scale = adaptor->y_scale; + float slant = adaptor->slant; - st->current_x *= x_scale; - st->current_y *= y_scale; + st->current_x = st->current_x * x_scale + st->current_y * slant; + st->current_y = st->current_y * y_scale; adaptor->draw_funcs->emit_cubic_to (adaptor->draw_data, *st, - x_scale * control1_x, y_scale * control1_y, - x_scale * control2_x, y_scale * control2_y, - x_scale * to_x, y_scale * to_y); + x_scale * control1_x + slant * control1_y, y_scale * control1_y, + x_scale * control2_x + slant * control2_y, y_scale * control2_y, + x_scale * to_x + slant * to_y, y_scale * to_y); } static void @@ -597,7 +614,7 @@ hb_draw_close_path_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, hb_draw_state_t *st, void *user_data HB_UNUSED) { - hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; + hb_font_draw_glyph_default_adaptor_t *adaptor = (hb_font_draw_glyph_default_adaptor_t *) draw_data; adaptor->draw_funcs->emit_close_path (adaptor->draw_data, *st); } @@ -613,25 +630,50 @@ static const hb_draw_funcs_t _hb_draw_funcs_default = { }; static void -hb_font_get_glyph_shape_default (hb_font_t *font, +hb_font_draw_glyph_default (hb_font_t *font, void *font_data HB_UNUSED, hb_codepoint_t glyph, hb_draw_funcs_t *draw_funcs, void *draw_data, void *user_data HB_UNUSED) { - hb_font_get_glyph_shape_default_adaptor_t adaptor = { + hb_font_draw_glyph_default_adaptor_t adaptor = { draw_funcs, draw_data, - (float) font->x_scale / (float) font->parent->x_scale, - (float) font->y_scale / (float) font->parent->y_scale + font->parent->x_scale ? (float) font->x_scale / (float) font->parent->x_scale : 0.f, + font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0.f, + font->parent->y_scale ? (font->slant - font->parent->slant) * + (float) font->x_scale / (float) font->parent->y_scale : 0.f }; - font->parent->get_glyph_shape (glyph, + font->parent->draw_glyph (glyph, const_cast (&_hb_draw_funcs_default), &adaptor); } +static void +hb_font_paint_glyph_default (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, + void *paint_data, + unsigned int palette, + hb_color_t foreground, + void *user_data) +{ + paint_funcs->push_transform (paint_data, + font->parent->x_scale ? (float) font->x_scale / (float) font->parent->x_scale : 0.f, + font->parent->y_scale ? (font->slant - font->parent->slant) * + (float) font->x_scale / (float) font->parent->y_scale : 0.f, + 0.f, + font->parent->y_scale ? (float) font->y_scale / (float) font->parent->y_scale : 0.f, + 0.f, 0.f); + + font->parent->paint_glyph (glyph, paint_funcs, paint_data, palette, foreground); + + paint_funcs->pop_transform (paint_data); +} + DEFINE_NULL_INSTANCE (hb_font_funcs_t) = { HB_OBJECT_HEADER_STATIC, @@ -640,7 +682,7 @@ DEFINE_NULL_INSTANCE (hb_font_funcs_t) = nullptr, { { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil, +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_nil, HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT } @@ -654,7 +696,7 @@ static const hb_font_funcs_t _hb_font_funcs_default = { nullptr, { { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_default, +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_default, HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT } @@ -732,7 +774,7 @@ hb_font_funcs_destroy (hb_font_funcs_t *ffuncs) if (ffuncs->destroy) { -#define HB_FONT_FUNC_IMPLEMENT(name) if (ffuncs->destroy->name) \ +#define HB_FONT_FUNC_IMPLEMENT(get_,name) if (ffuncs->destroy->name) \ ffuncs->destroy->name (!ffuncs->user_data ? nullptr : ffuncs->user_data->name); HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT @@ -754,7 +796,7 @@ hb_font_funcs_destroy (hb_font_funcs_t *ffuncs) * * Attaches a user-data key/data pair to the specified font-functions structure. * - * Return value: %true if success, %false otherwise + * Return value: `true` if success, `false` otherwise * * Since: 0.9.2 **/ @@ -781,8 +823,8 @@ hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, * Since: 0.9.2 **/ void * -hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, - hb_user_data_key_t *key) +hb_font_funcs_get_user_data (const hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key) { return hb_object_get_user_data (ffuncs, key); } @@ -811,7 +853,7 @@ hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs) * * Tests whether a font-functions structure is immutable. * - * Return value: %true if @ffuncs is immutable, %false otherwise + * Return value: `true` if @ffuncs is immutable, `false` otherwise * * Since: 0.9.2 **/ @@ -822,59 +864,82 @@ hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs) } -#define HB_FONT_FUNC_IMPLEMENT(name) \ +static bool +_hb_font_funcs_set_preamble (hb_font_funcs_t *ffuncs, + bool func_is_null, + void **user_data, + hb_destroy_func_t *destroy) +{ + if (hb_object_is_immutable (ffuncs)) + { + if (*destroy) + (*destroy) (*user_data); + return false; + } + + if (func_is_null) + { + if (*destroy) + (*destroy) (*user_data); + *destroy = nullptr; + *user_data = nullptr; + } + + return true; +} + +static bool +_hb_font_funcs_set_middle (hb_font_funcs_t *ffuncs, + void *user_data, + hb_destroy_func_t destroy) +{ + if (user_data && !ffuncs->user_data) + { + ffuncs->user_data = (decltype (ffuncs->user_data)) hb_calloc (1, sizeof (*ffuncs->user_data)); + if (unlikely (!ffuncs->user_data)) + goto fail; + } + if (destroy && !ffuncs->destroy) + { + ffuncs->destroy = (decltype (ffuncs->destroy)) hb_calloc (1, sizeof (*ffuncs->destroy)); + if (unlikely (!ffuncs->destroy)) + goto fail; + } + + return true; + +fail: + if (destroy) + (destroy) (user_data); + return false; +} + +#define HB_FONT_FUNC_IMPLEMENT(get_,name) \ \ void \ hb_font_funcs_set_##name##_func (hb_font_funcs_t *ffuncs, \ - hb_font_get_##name##_func_t func, \ + hb_font_##get_##name##_func_t func, \ void *user_data, \ hb_destroy_func_t destroy) \ { \ - if (hb_object_is_immutable (ffuncs)) \ - goto fail; \ - \ - if (!func) \ - { \ - if (destroy) \ - destroy (user_data); \ - destroy = nullptr; \ - user_data = nullptr; \ - } \ + if (!_hb_font_funcs_set_preamble (ffuncs, !func, &user_data, &destroy))\ + return; \ \ if (ffuncs->destroy && ffuncs->destroy->name) \ ffuncs->destroy->name (!ffuncs->user_data ? nullptr : ffuncs->user_data->name); \ \ - if (user_data && !ffuncs->user_data) \ - { \ - ffuncs->user_data = (decltype (ffuncs->user_data)) hb_calloc (1, sizeof (*ffuncs->user_data)); \ - if (unlikely (!ffuncs->user_data)) \ - goto fail; \ - } \ - if (destroy && !ffuncs->destroy) \ - { \ - ffuncs->destroy = (decltype (ffuncs->destroy)) hb_calloc (1, sizeof (*ffuncs->destroy)); \ - if (unlikely (!ffuncs->destroy)) \ - goto fail; \ - } \ + if (!_hb_font_funcs_set_middle (ffuncs, user_data, destroy)) \ + return; \ \ - if (func) { \ + if (func) \ ffuncs->get.f.name = func; \ - if (ffuncs->user_data) \ - ffuncs->user_data->name = user_data; \ - if (ffuncs->destroy) \ - ffuncs->destroy->name = destroy; \ - } else { \ - ffuncs->get.f.name = hb_font_get_##name##_default; \ - if (ffuncs->user_data) \ - ffuncs->user_data->name = nullptr; \ - if (ffuncs->destroy) \ - ffuncs->destroy->name = nullptr; \ - } \ - return; \ + else \ + ffuncs->get.f.name = hb_font_##get_##name##_default; \ \ -fail: \ - if (destroy) \ - destroy (user_data); \ + if (ffuncs->user_data) \ + ffuncs->user_data->name = user_data; \ + if (ffuncs->destroy) \ + ffuncs->destroy->name = destroy; \ } HB_FONT_FUNCS_IMPLEMENT_CALLBACKS @@ -903,7 +968,7 @@ hb_font_t::has_func (unsigned int i) * Fetches the extents for a specified font, for horizontal * text segments. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 1.1.3 **/ @@ -922,7 +987,7 @@ hb_font_get_h_extents (hb_font_t *font, * Fetches the extents for a specified font, for vertical * text segments. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 1.1.3 **/ @@ -946,7 +1011,7 @@ hb_font_get_v_extents (hb_font_t *font, * If @variation_selector is 0, calls hb_font_get_nominal_glyph(); * otherwise calls hb_font_get_variation_glyph(). * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ @@ -974,7 +1039,7 @@ hb_font_get_glyph (hb_font_t *font, * for code points modified by variation selectors. For variation-selector * support, user hb_font_get_variation_glyph() or use hb_font_get_glyph(). * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 1.2.3 **/ @@ -996,7 +1061,8 @@ hb_font_get_nominal_glyph (hb_font_t *font, * @glyph_stride: The stride between successive glyph IDs * * Fetches the nominal glyph IDs for a sequence of Unicode code points. Glyph - * IDs must be returned in a #hb_codepoint_t output parameter. + * IDs must be returned in a #hb_codepoint_t output parameter. Stopes at the + * first unsupported glyph ID. * * Return value: the number of code points processed * @@ -1026,7 +1092,7 @@ hb_font_get_nominal_glyphs (hb_font_t *font, * by the specified variation-selector code point, in the specified * font. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 1.2.3 **/ @@ -1136,7 +1202,7 @@ hb_font_get_glyph_v_advances (hb_font_t* font, * Fetches the (X,Y) coordinates of the origin for a glyph ID * in the specified font, for horizontal text segments. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ @@ -1159,7 +1225,7 @@ hb_font_get_glyph_h_origin (hb_font_t *font, * Fetches the (X,Y) coordinates of the origin for a glyph ID * in the specified font, for vertical text segments. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ @@ -1232,7 +1298,7 @@ hb_font_get_glyph_v_kerning (hb_font_t *font, * Fetches the #hb_glyph_extents_t data for a glyph ID * in the specified font. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ @@ -1255,7 +1321,7 @@ hb_font_get_glyph_extents (hb_font_t *font, * Fetches the (x,y) coordinates of a specified contour-point index * in the specified glyph, within the specified font. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ @@ -1278,7 +1344,10 @@ hb_font_get_glyph_contour_point (hb_font_t *font, * * Fetches the glyph-name string for a glyph ID in the specified @font. * - * Return value: %true if data found, %false otherwise + * According to the OpenType specification, glyph names are limited to 63 + * characters and can only contain (a subset of) ASCII. + * + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ @@ -1302,7 +1371,7 @@ hb_font_get_glyph_name (hb_font_t *font, * * Note: @len == -1 means the name string is null-terminated. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ @@ -1323,17 +1392,71 @@ hb_font_get_glyph_from_name (hb_font_t *font, * @draw_data: User data to pass to draw callbacks * * Fetches the glyph shape that corresponds to a glyph in the specified @font. - * The shape is returned by way of calls to the callsbacks of the @dfuncs + * The shape is returned by way of calls to the callbacks of the @dfuncs * objects, with @draw_data passed to them. * * Since: 4.0.0 - **/ + * Deprecated: 7.0.0: Use hb_font_draw_glyph() instead + */ void hb_font_get_glyph_shape (hb_font_t *font, hb_codepoint_t glyph, hb_draw_funcs_t *dfuncs, void *draw_data) { - font->get_glyph_shape (glyph, dfuncs, draw_data); + hb_font_draw_glyph (font, glyph, dfuncs, draw_data); +} + +/** + * hb_font_draw_glyph: + * @font: #hb_font_t to work upon + * @glyph: : The glyph ID + * @dfuncs: #hb_draw_funcs_t to draw to + * @draw_data: User data to pass to draw callbacks + * + * Draws the outline that corresponds to a glyph in the specified @font. + * + * The outline is returned by way of calls to the callbacks of the @dfuncs + * objects, with @draw_data passed to them. + * + * Since: 7.0.0 + **/ +void +hb_font_draw_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data) +{ + font->draw_glyph (glyph, dfuncs, draw_data); +} + +/** + * hb_font_paint_glyph: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID + * @pfuncs: #hb_paint_funcs_t to paint with + * @paint_data: User data to pass to paint callbacks + * @palette_index: The index of the font's color palette to use + * @foreground: The foreground color, unpremultipled + * + * Paints the glyph. + * + * The painting instructions are returned by way of calls to + * the callbacks of the @funcs object, with @paint_data passed + * to them. + * + * If the font has color palettes (see hb_ot_color_has_palettes()), + * then @palette_index selects the palette to use. If the font only + * has one palette, this will be 0. + * + * Since: 7.0.0 + */ +void +hb_font_paint_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *pfuncs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground) +{ + font->paint_glyph (glyph, pfuncs, paint_data, palette_index, foreground); } /* A bit higher-level, and with fallback */ @@ -1537,7 +1660,7 @@ hb_font_get_glyph_kerning_for_direction (hb_font_t *font, * Calls the appropriate direction-specific variant (horizontal * or vertical) depending on the value of @direction. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ @@ -1566,7 +1689,7 @@ hb_font_get_glyph_extents_for_origin (hb_font_t *font, * Calls the appropriate direction-specific variant (horizontal * or vertical) depending on the value of @direction. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ @@ -1594,6 +1717,9 @@ hb_font_get_glyph_contour_point_for_origin (hb_font_t *font, * If the glyph ID has no name in @font, a string of the form `gidDDD` is * generated, with `DDD` being the glyph ID. * + * According to the OpenType specification, glyph names are limited to 63 + * characters and can only contain (a subset of) ASCII. + * * Since: 0.9.2 **/ void @@ -1617,7 +1743,7 @@ hb_font_glyph_to_string (hb_font_t *font, * * Note: @len == -1 means the string is null-terminated. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 0.9.2 **/ @@ -1647,8 +1773,13 @@ DEFINE_NULL_INSTANCE (hb_font_t) = 1000, /* x_scale */ 1000, /* y_scale */ - 0., /* slant */ - 0., /* slant_xy; */ + 0.f, /* x_embolden */ + 0.f, /* y_embolden */ + true, /* embolden_in_place */ + 0, /* x_strength */ + 0, /* y_strength */ + 0.f, /* slant */ + 0.f, /* slant_xy; */ 1.f, /* x_multf */ 1.f, /* y_multf */ 1<<16, /* x_mult */ @@ -1658,6 +1789,7 @@ DEFINE_NULL_INSTANCE (hb_font_t) = 0, /* y_ppem */ 0, /* ptem */ + HB_FONT_NO_VAR_NAMED_INSTANCE, /* instance_index */ 0, /* num_coords */ nullptr, /* coords */ nullptr, /* design_coords */ @@ -1675,6 +1807,7 @@ _hb_font_create (hb_face_t *face) if (unlikely (!face)) face = hb_face_get_empty (); + if (!(font = hb_object_create ())) return hb_font_get_empty (); @@ -1684,8 +1817,10 @@ _hb_font_create (hb_face_t *face) font->klass = hb_font_funcs_get_empty (); font->data.init0 (font); font->x_scale = font->y_scale = face->get_upem (); + font->embolden_in_place = true; font->x_multf = font->y_multf = 1.f; font->x_mult = font->y_mult = 1 << 16; + font->instance_index = HB_FONT_NO_VAR_NAMED_INSTANCE; return font; } @@ -1767,6 +1902,9 @@ hb_font_create_sub_font (hb_font_t *parent) font->x_scale = parent->x_scale; font->y_scale = parent->y_scale; + font->x_embolden = parent->x_embolden; + font->y_embolden = parent->y_embolden; + font->embolden_in_place = parent->embolden_in_place; font->slant = parent->slant; font->x_ppem = parent->x_ppem; font->y_ppem = parent->y_ppem; @@ -1779,8 +1917,8 @@ hb_font_create_sub_font (hb_font_t *parent) float *design_coords = (float *) hb_calloc (num_coords, sizeof (parent->design_coords[0])); if (likely (coords && design_coords)) { - memcpy (coords, parent->coords, num_coords * sizeof (parent->coords[0])); - memcpy (design_coords, parent->design_coords, num_coords * sizeof (parent->design_coords[0])); + hb_memcpy (coords, parent->coords, num_coords * sizeof (parent->coords[0])); + hb_memcpy (design_coords, parent->design_coords, num_coords * sizeof (parent->design_coords[0])); _hb_font_adopt_var_coords (font, coords, design_coords, num_coords); } else @@ -1866,7 +2004,7 @@ hb_font_destroy (hb_font_t *font) * * Attaches a user-data key/data pair to the specified font object. * - * Return value: %true if success, %false otherwise + * Return value: `true` if success, `false` otherwise * * Since: 0.9.2 **/ @@ -1896,7 +2034,7 @@ hb_font_set_user_data (hb_font_t *font, * Since: 0.9.2 **/ void * -hb_font_get_user_data (hb_font_t *font, +hb_font_get_user_data (const hb_font_t *font, hb_user_data_key_t *key) { return hb_object_get_user_data (font, key); @@ -1928,7 +2066,7 @@ hb_font_make_immutable (hb_font_t *font) * * Tests whether a font object is immutable. * - * Return value: %true if @font is immutable, %false otherwise + * Return value: `true` if @font is immutable, `false` otherwise * * Since: 0.9.2 **/ @@ -1948,7 +2086,7 @@ hb_font_is_immutable (hb_font_t *font) * * Return value: serial number * - * Since: 4.4.0. + * Since: 4.4.0 **/ unsigned int hb_font_get_serial (hb_font_t *font) @@ -1964,7 +2102,7 @@ hb_font_get_serial (hb_font_t *font) * This has the effect of increasing the serial as returned * by hb_font_get_serial(), which invalidates internal caches. * - * Since: 4.4.0. + * Since: 4.4.0 **/ void hb_font_changed (hb_font_t *font) @@ -2156,6 +2294,31 @@ hb_font_set_funcs_data (hb_font_t *font, * * Sets the horizontal and vertical scale of a font. * + * The font scale is a number related to, but not the same as, + * font size. Typically the client establishes a scale factor + * to be used between the two. For example, 64, or 256, which + * would be the fractional-precision part of the font scale. + * This is necessary because #hb_position_t values are integer + * types and you need to leave room for fractional values + * in there. + * + * For example, to set the font size to 20, with 64 + * levels of fractional precision you would call + * `hb_font_set_scale(font, 20 * 64, 20 * 64)`. + * + * In the example above, even what font size 20 means is up to + * you. It might be 20 pixels, or 20 points, or 20 millimeters. + * HarfBuzz does not care about that. You can set the point + * size of the font using hb_font_set_ptem(), and the pixel + * size using hb_font_set_ppem(). + * + * The choice of scale is yours but needs to be consistent between + * what you set here, and what you expect out of #hb_position_t + * as well has draw / paint API output values. + * + * Fonts default to a scale equal to the UPEM value of their face. + * A font with this setting is sometimes called an "unscaled" font. + * * Since: 0.9.2 **/ void @@ -2201,7 +2364,11 @@ hb_font_get_scale (hb_font_t *font, * @x_ppem: Horizontal ppem value to assign * @y_ppem: Vertical ppem value to assign * - * Sets the horizontal and vertical pixels-per-em (ppem) of a font. + * Sets the horizontal and vertical pixels-per-em (PPEM) of a font. + * + * These values are used for pixel-size-specific adjustment to + * shaping and draw results, though for the most part they are + * unused and can be left unset. * * Since: 0.9.2 **/ @@ -2285,6 +2452,76 @@ hb_font_get_ptem (hb_font_t *font) return font->ptem; } +/** + * hb_font_set_synthetic_bold: + * @font: #hb_font_t to work upon + * @x_embolden: the amount to embolden horizontally + * @y_embolden: the amount to embolden vertically + * @in_place: whether to embolden glyphs in-place + * + * Sets the "synthetic boldness" of a font. + * + * Positive values for @x_embolden / @y_embolden make a font + * bolder, negative values thinner. Typical values are in the + * 0.01 to 0.05 range. The default value is zero. + * + * Synthetic boldness is applied by offsetting the contour + * points of the glyph shape. + * + * Synthetic boldness is applied when rendering a glyph via + * hb_font_draw_glyph(). + * + * If @in_place is `false`, then glyph advance-widths are also + * adjusted, otherwise they are not. The in-place mode is + * useful for simulating [font grading](https://fonts.google.com/knowledge/glossary/grade). + * + * + * Since: 7.0.0 + **/ +void +hb_font_set_synthetic_bold (hb_font_t *font, + float x_embolden, + float y_embolden, + hb_bool_t in_place) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->x_embolden == x_embolden && + font->y_embolden == y_embolden && + font->embolden_in_place == (bool) in_place) + return; + + font->serial++; + + font->x_embolden = x_embolden; + font->y_embolden = y_embolden; + font->embolden_in_place = in_place; + font->mults_changed (); +} + +/** + * hb_font_get_synthetic_bold: + * @font: #hb_font_t to work upon + * @x_embolden: (out): return location for horizontal value + * @y_embolden: (out): return location for vertical value + * @in_place: (out): return location for in-place value + * + * Fetches the "synthetic boldness" parameters of a font. + * + * Since: 7.0.0 + **/ +void +hb_font_get_synthetic_bold (hb_font_t *font, + float *x_embolden, + float *y_embolden, + hb_bool_t *in_place) +{ + if (x_embolden) *x_embolden = font->x_embolden; + if (y_embolden) *y_embolden = font->y_embolden; + if (in_place) *in_place = font->embolden_in_place; +} + /** * hb_font_set_synthetic_slant: * @font: #hb_font_t to work upon @@ -2297,9 +2534,8 @@ hb_font_get_ptem (hb_font_t *font) * HarfBuzz needs to know this value to adjust shaping results, * metrics, and style values to match the slanted rendering. * - * Note: The glyph shape fetched via the - * hb_font_get_glyph_shape() is slanted to reflect this value - * as well. + * Note: The glyph shape fetched via the hb_font_draw_glyph() + * function is slanted to reflect this value as well. * * Note: The slant value is a ratio. For example, a * 20% slant would be represented as a 0.2 value. @@ -2366,7 +2602,7 @@ hb_font_set_variations (hb_font_t *font, font->serial_coords = ++font->serial; - if (!variations_length) + if (!variations_length && font->instance_index == HB_FONT_NO_VAR_NAMED_INSTANCE) { hb_font_set_var_coords_normalized (font, nullptr, 0); return; @@ -2386,19 +2622,30 @@ hb_font_set_variations (hb_font_t *font, return; } + /* Initialize design coords. */ + for (unsigned int i = 0; i < coords_length; i++) + design_coords[i] = axes[i].get_default (); + if (font->instance_index != HB_FONT_NO_VAR_NAMED_INSTANCE) + { + unsigned count = coords_length; + /* This may fail if index is out-of-range; + * That's why we initialize design_coords from fvar above + * unconditionally. */ + hb_ot_var_named_instance_get_design_coords (font->face, font->instance_index, + &count, design_coords); + } + for (unsigned int i = 0; i < variations_length; i++) { const auto tag = variations[i].tag; const auto v = variations[i].value; for (unsigned axis_index = 0; axis_index < coords_length; axis_index++) if (axes[axis_index].axisTag == tag) - { design_coords[axis_index] = v; - normalized[axis_index] = fvar.normalize_axis_value (axis_index, v); - } } font->face->table.avar->map_coords (normalized, coords_length); + hb_ot_var_normalize_coords (font->face, coords_length, design_coords, normalized); _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); } @@ -2438,7 +2685,7 @@ hb_font_set_var_coords_design (hb_font_t *font, } if (coords_length) - memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0])); + hb_memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0])); hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized); _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); @@ -2449,28 +2696,40 @@ hb_font_set_var_coords_design (hb_font_t *font, * @font: a font. * @instance_index: named instance index. * - * Sets design coords of a font from a named instance index. + * Sets design coords of a font from a named-instance index. * * Since: 2.6.0 */ void hb_font_set_var_named_instance (hb_font_t *font, - unsigned instance_index) + unsigned int instance_index) { if (hb_object_is_immutable (font)) return; - font->serial_coords = ++font->serial; + if (font->instance_index == instance_index) + return; - unsigned int coords_length = hb_ot_var_named_instance_get_design_coords (font->face, instance_index, nullptr, nullptr); + font->serial_coords = ++font->serial; - float *coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; - if (unlikely (coords_length && !coords)) - return; + font->instance_index = instance_index; + hb_font_set_variations (font, nullptr, 0); +} - hb_ot_var_named_instance_get_design_coords (font->face, instance_index, &coords_length, coords); - hb_font_set_var_coords_design (font, coords, coords_length); - hb_free (coords); +/** + * hb_font_get_var_named_instance: + * @font: a font. + * + * Returns the currently-set named-instance index of the font. + * + * Return value: Named-instance index or %HB_FONT_NO_VAR_NAMED_INSTANCE. + * + * Since: 7.0.0 + **/ +unsigned int +hb_font_get_var_named_instance (hb_font_t *font) +{ + return font->instance_index; } /** @@ -2514,8 +2773,8 @@ hb_font_set_var_coords_normalized (hb_font_t *font, if (coords_length) { - memcpy (copy, coords, coords_length * sizeof (coords[0])); - memcpy (unmapped, coords, coords_length * sizeof (coords[0])); + hb_memcpy (copy, coords, coords_length * sizeof (coords[0])); + hb_memcpy (unmapped, coords, coords_length * sizeof (coords[0])); } /* Best effort design coords simulation */ @@ -2719,3 +2978,13 @@ hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, trampoline_destroy); } #endif + + +void +hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_shape_func_t func, + void *user_data, + hb_destroy_func_t destroy /* May be NULL. */) +{ + hb_font_funcs_set_draw_glyph_func (ffuncs, func, user_data, destroy); +} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.h b/src/java.desktop/share/native/libharfbuzz/hb-font.h index a6aeeef4770..77baf74d191 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.h @@ -34,18 +34,10 @@ #include "hb-common.h" #include "hb-face.h" #include "hb-draw.h" +#include "hb-paint.h" HB_BEGIN_DECLS -/** - * hb_font_t: - * - * Data type for holding fonts. - * - */ -typedef struct hb_font_t hb_font_t; - - /* * hb_font_funcs_t */ @@ -86,8 +78,8 @@ hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, HB_EXTERN void * -hb_font_funcs_get_user_data (hb_font_funcs_t *ffuncs, - hb_user_data_key_t *key); +hb_font_funcs_get_user_data (const hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key); HB_EXTERN void @@ -97,7 +89,7 @@ HB_EXTERN hb_bool_t hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs); -/* font and glyph extents */ +/* font extents */ /** * hb_font_extents_t: @@ -126,24 +118,6 @@ typedef struct hb_font_extents_t { hb_position_t reserved1; } hb_font_extents_t; -/** - * hb_glyph_extents_t: - * @x_bearing: Distance from the x-origin to the left extremum of the glyph. - * @y_bearing: Distance from the top extremum of the glyph to the y-origin. - * @width: Distance from the left extremum of the glyph to the right extremum. - * @height: Distance from the top extremum of the glyph to the bottom extremum. - * - * Glyph extent values, measured in font units. - * - * Note that @height is negative, in coordinate systems that grow up. - **/ -typedef struct hb_glyph_extents_t { - hb_position_t x_bearing; - hb_position_t y_bearing; - hb_position_t width; - hb_position_t height; -} hb_glyph_extents_t; - /* func types */ /** @@ -198,7 +172,7 @@ typedef hb_font_get_font_extents_func_t hb_font_get_font_v_extents_func_t; * This method should retrieve the nominal glyph ID for a specified Unicode code * point. Glyph IDs must be returned in a #hb_codepoint_t output parameter. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * **/ typedef hb_bool_t (*hb_font_get_nominal_glyph_func_t) (hb_font_t *font, void *font_data, @@ -221,7 +195,7 @@ typedef hb_bool_t (*hb_font_get_nominal_glyph_func_t) (hb_font_t *font, void *fo * followed by a specified Variation Selector code point. Glyph IDs must be * returned in a #hb_codepoint_t output parameter. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * **/ typedef hb_bool_t (*hb_font_get_variation_glyph_func_t) (hb_font_t *font, void *font_data, @@ -362,7 +336,7 @@ typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_v_advances_func_t; * origin for a glyph. Each coordinate must be returned in an #hb_position_t * output parameter. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * **/ typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *font_data, @@ -434,7 +408,7 @@ typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t; * This method should retrieve the extents for a specified glyph. Extents must be * returned in an #hb_glyph_extents output parameter. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * **/ typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *font_data, @@ -458,7 +432,7 @@ typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *fo * specified contour point in a glyph. Each coordinate must be returned as * an #hb_position_t output parameter. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * **/ typedef hb_bool_t (*hb_font_get_glyph_contour_point_func_t) (hb_font_t *font, void *font_data, @@ -481,7 +455,7 @@ typedef hb_bool_t (*hb_font_get_glyph_contour_point_func_t) (hb_font_t *font, vo * This method should retrieve the glyph name that corresponds to a * glyph ID. The name should be returned in a string output parameter. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * **/ typedef hb_bool_t (*hb_font_get_glyph_name_func_t) (hb_font_t *font, void *font_data, @@ -503,7 +477,7 @@ typedef hb_bool_t (*hb_font_get_glyph_name_func_t) (hb_font_t *font, void *font_ * This method should retrieve the glyph ID that corresponds to a glyph-name * string. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * **/ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void *font_data, @@ -523,13 +497,53 @@ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void * * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * * Since: 4.0.0 - * + * Deprecated: 7.0.0: Use #hb_font_draw_glyph_func_t instead **/ typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, hb_draw_funcs_t *draw_funcs, void *draw_data, void *user_data); +/** + * hb_font_draw_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @draw_funcs: The draw functions to send the shape data to + * @draw_data: The data accompanying the draw functions + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * Since: 7.0.0 + * + **/ +typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data); + +/** + * hb_font_paint_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @paint_funcs: The paint functions to use + * @paint_data: The data accompanying the paint functions + * @palette_index: The color palette to use + * @foreground: The foreground color + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * Since: 7.0.0 + */ +typedef void (*hb_font_paint_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data); /* func setters */ @@ -796,15 +810,50 @@ hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs, * @user_data: Data to pass to @func * @destroy: (nullable): The function to call when @user_data is not needed anymore * - * Sets the implementation function for #hb_font_get_glyph_shape_func_t. + * Sets the implementation function for #hb_font_get_glyph_shape_func_t, + * which is the same as #hb_font_draw_glyph_func_t. * * Since: 4.0.0 + * Deprecated: 7.0.0: Use hb_font_funcs_set_draw_glyph_func() instead **/ HB_EXTERN void hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_shape_func_t func, void *user_data, hb_destroy_func_t destroy); +/** + * hb_font_funcs_set_draw_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_draw_glyph_func_t, + * which is the same as #hb_font_get_glyph_shape_func_t. + * + * Since: 7.0.0 + **/ +HB_EXTERN void +hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_draw_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_paint_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is no longer needed + * + * Sets the implementation function for #hb_font_paint_glyph_func_t. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_font_funcs_set_paint_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_paint_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + /* func dispatch */ HB_EXTERN hb_bool_t @@ -890,6 +939,17 @@ hb_font_get_glyph_shape (hb_font_t *font, hb_codepoint_t glyph, hb_draw_funcs_t *dfuncs, void *draw_data); +HB_EXTERN void +hb_font_draw_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data); + +HB_EXTERN void +hb_font_paint_glyph (hb_font_t *font, + hb_codepoint_t glyph, + hb_paint_funcs_t *pfuncs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground); /* high-level funcs, with fallback */ @@ -993,7 +1053,7 @@ hb_font_set_user_data (hb_font_t *font, HB_EXTERN void * -hb_font_get_user_data (hb_font_t *font, +hb_font_get_user_data (const hb_font_t *font, hb_user_data_key_t *key); HB_EXTERN void @@ -1069,6 +1129,16 @@ hb_font_set_ptem (hb_font_t *font, float ptem); HB_EXTERN float hb_font_get_ptem (hb_font_t *font); +HB_EXTERN void +hb_font_set_synthetic_bold (hb_font_t *font, + float x_embolden, float y_embolden, + hb_bool_t in_place); + +HB_EXTERN void +hb_font_get_synthetic_bold (hb_font_t *font, + float *x_embolden, float *y_embolden, + hb_bool_t *in_place); + HB_EXTERN void hb_font_set_synthetic_slant (hb_font_t *font, float slant); @@ -1098,10 +1168,23 @@ HB_EXTERN const int * hb_font_get_var_coords_normalized (hb_font_t *font, unsigned int *length); +/** + * HB_FONT_NO_VAR_NAMED_INSTANCE: + * + * Constant signifying that a font does not have any + * named-instance index set. This is the default of + * a font. + * + * Since: 7.0.0 + */ +#define HB_FONT_NO_VAR_NAMED_INSTANCE 0xFFFFFFFF + HB_EXTERN void hb_font_set_var_named_instance (hb_font_t *font, - unsigned instance_index); + unsigned int instance_index); +HB_EXTERN unsigned int +hb_font_get_var_named_instance (hb_font_t *font); HB_END_DECLS diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.hh b/src/java.desktop/share/native/libharfbuzz/hb-font.hh index 3af9d74012a..c3198830cd3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.hh @@ -40,24 +40,25 @@ */ #define HB_FONT_FUNCS_IMPLEMENT_CALLBACKS \ - HB_FONT_FUNC_IMPLEMENT (font_h_extents) \ - HB_FONT_FUNC_IMPLEMENT (font_v_extents) \ - HB_FONT_FUNC_IMPLEMENT (nominal_glyph) \ - HB_FONT_FUNC_IMPLEMENT (nominal_glyphs) \ - HB_FONT_FUNC_IMPLEMENT (variation_glyph) \ - HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \ - HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \ - HB_FONT_FUNC_IMPLEMENT (glyph_h_advances) \ - HB_FONT_FUNC_IMPLEMENT (glyph_v_advances) \ - HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \ - HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \ - HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \ - HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning)) \ - HB_FONT_FUNC_IMPLEMENT (glyph_extents) \ - HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \ - HB_FONT_FUNC_IMPLEMENT (glyph_name) \ - HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \ - HB_FONT_FUNC_IMPLEMENT (glyph_shape) \ + HB_FONT_FUNC_IMPLEMENT (get_,font_h_extents) \ + HB_FONT_FUNC_IMPLEMENT (get_,font_v_extents) \ + HB_FONT_FUNC_IMPLEMENT (get_,nominal_glyph) \ + HB_FONT_FUNC_IMPLEMENT (get_,nominal_glyphs) \ + HB_FONT_FUNC_IMPLEMENT (get_,variation_glyph) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_advance) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_advance) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_advances) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_advances) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_origin) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_origin) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_kerning) \ + HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_kerning)) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_extents) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_contour_point) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_name) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_from_name) \ + HB_FONT_FUNC_IMPLEMENT (,draw_glyph) \ + HB_FONT_FUNC_IMPLEMENT (,paint_glyph) \ /* ^--- Add new callbacks here */ struct hb_font_funcs_t @@ -65,13 +66,13 @@ struct hb_font_funcs_t hb_object_header_t header; struct { -#define HB_FONT_FUNC_IMPLEMENT(name) void *name; +#define HB_FONT_FUNC_IMPLEMENT(get_,name) void *name; HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT } *user_data; struct { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_destroy_func_t name; +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_destroy_func_t name; HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT } *destroy; @@ -79,12 +80,12 @@ struct hb_font_funcs_t /* Don't access these directly. Call font->get_*() instead. */ union get_t { struct get_funcs_t { -#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_func_t name; +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_func_t name; HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT } f; void (*array[0 -#define HB_FONT_FUNC_IMPLEMENT(name) +1 +#define HB_FONT_FUNC_IMPLEMENT(get_,name) +1 HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT ]) (); @@ -112,8 +113,16 @@ struct hb_font_t int32_t x_scale; int32_t y_scale; + + float x_embolden; + float y_embolden; + bool embolden_in_place; + int32_t x_strength; /* x_embolden, in scaled units. */ + int32_t y_strength; /* y_embolden, in scaled units. */ + float slant; float slant_xy; + float x_multf; float y_multf; int64_t x_mult; @@ -125,6 +134,7 @@ struct hb_font_t float ptem; /* Font variation coordinates. */ + unsigned int instance_index; unsigned int num_coords; int *coords; float *design_coords; @@ -179,6 +189,42 @@ struct hb_font_t *y = parent_scale_y_position (*y); } + void scale_glyph_extents (hb_glyph_extents_t *extents) + { + float x1 = em_fscale_x (extents->x_bearing); + float y1 = em_fscale_y (extents->y_bearing); + float x2 = em_fscale_x (extents->x_bearing + extents->width); + float y2 = em_fscale_y (extents->y_bearing + extents->height); + + /* Apply slant. */ + if (slant_xy) + { + x1 += hb_min (y1 * slant_xy, y2 * slant_xy); + x2 += hb_max (y1 * slant_xy, y2 * slant_xy); + } + + extents->x_bearing = floorf (x1); + extents->y_bearing = floorf (y1); + extents->width = ceilf (x2) - extents->x_bearing; + extents->height = ceilf (y2) - extents->y_bearing; + + if (x_strength || y_strength) + { + /* Y */ + int y_shift = y_strength; + if (y_scale < 0) y_shift = -y_shift; + extents->y_bearing += y_shift; + extents->height -= y_shift; + + /* X */ + int x_shift = x_strength; + if (x_scale < 0) x_shift = -x_shift; + if (embolden_in_place) + extents->x_bearing -= x_shift / 2; + extents->width += x_shift; + } + } + /* Public getters */ @@ -186,7 +232,7 @@ struct hb_font_t HB_INTERNAL bool has_func_set (unsigned int i); /* has_* ... */ -#define HB_FONT_FUNC_IMPLEMENT(name) \ +#define HB_FONT_FUNC_IMPLEMENT(get_,name) \ bool \ has_##name##_func () \ { \ @@ -206,14 +252,14 @@ struct hb_font_t hb_bool_t get_font_h_extents (hb_font_extents_t *extents) { - memset (extents, 0, sizeof (*extents)); + hb_memset (extents, 0, sizeof (*extents)); return klass->get.f.font_h_extents (this, user_data, extents, !klass->user_data ? nullptr : klass->user_data->font_h_extents); } hb_bool_t get_font_v_extents (hb_font_extents_t *extents) { - memset (extents, 0, sizeof (*extents)); + hb_memset (extents, 0, sizeof (*extents)); return klass->get.f.font_v_extents (this, user_data, extents, !klass->user_data ? nullptr : klass->user_data->font_v_extents); @@ -342,7 +388,7 @@ struct hb_font_t hb_bool_t get_glyph_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) { - memset (extents, 0, sizeof (*extents)); + hb_memset (extents, 0, sizeof (*extents)); return klass->get.f.glyph_extents (this, user_data, glyph, extents, @@ -380,15 +426,26 @@ struct hb_font_t !klass->user_data ? nullptr : klass->user_data->glyph_from_name); } - void get_glyph_shape (hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, void *draw_data) + void draw_glyph (hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data) { - klass->get.f.glyph_shape (this, user_data, - glyph, - draw_funcs, draw_data, - !klass->user_data ? nullptr : klass->user_data->glyph_shape); + klass->get.f.draw_glyph (this, user_data, + glyph, + draw_funcs, draw_data, + !klass->user_data ? nullptr : klass->user_data->draw_glyph); } + void paint_glyph (hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground) + { + klass->get.f.paint_glyph (this, user_data, + glyph, + paint_funcs, paint_data, + palette, foreground, + !klass->user_data ? nullptr : klass->user_data->paint_glyph); + } /* A bit higher-level, and with fallback */ @@ -632,12 +689,17 @@ struct hb_font_t void mults_changed () { float upem = face->get_upem (); + x_multf = x_scale / upem; y_multf = y_scale / upem; bool x_neg = x_scale < 0; x_mult = (x_neg ? -((int64_t) -x_scale << 16) : ((int64_t) x_scale << 16)) / upem; bool y_neg = y_scale < 0; y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem; + + x_strength = fabsf (roundf (x_scale * x_embolden)); + y_strength = fabsf (roundf (y_scale * y_embolden)); + slant_xy = y_scale ? slant * x_scale / y_scale : 0.f; data.fini (); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc index 291d1e8d70c..93ef3b02d47 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc @@ -33,17 +33,22 @@ #include "hb-ft.h" +#include "hb-cache.hh" #include "hb-draw.hh" #include "hb-font.hh" #include "hb-machinery.hh" -#include "hb-cache.hh" #include "hb-ot-os2-table.hh" #include "hb-ot-shaper-arabic-pua.hh" +#include "hb-paint.hh" #include FT_ADVANCES_H #include FT_MULTIPLE_MASTERS_H #include FT_OUTLINE_H #include FT_TRUETYPE_TABLES_H +#include FT_SYNTHESIS_H +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300 +#include FT_COLOR_H +#endif /** @@ -80,16 +85,19 @@ */ +using hb_ft_advance_cache_t = hb_cache_t<16, 8, 8, false>; + struct hb_ft_font_t { int load_flags; bool symbol; /* Whether selected cmap is symbol cmap. */ bool unref; /* Whether to destroy ft_face when done. */ + bool transform; /* Whether to apply FT_Face's transform. */ - mutable hb_mutex_t lock; + mutable hb_mutex_t lock; /* Protects members below. */ FT_Face ft_face; mutable unsigned cached_serial; - mutable hb_advance_cache_t advance_cache; + mutable hb_ft_advance_cache_t advance_cache; }; static hb_ft_font_t * @@ -122,8 +130,6 @@ _hb_ft_font_destroy (void *data) { hb_ft_font_t *ft_font = (hb_ft_font_t *) data; - ft_font->advance_cache.fini (); - if (ft_font->unref) _hb_ft_face_destroy (ft_font->ft_face); @@ -136,32 +142,59 @@ _hb_ft_font_destroy (void *data) /* hb_font changed, update FT_Face. */ static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face) { + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + float x_mult = 1.f, y_mult = 1.f; + + if (font->x_scale < 0) x_mult = -x_mult; + if (font->y_scale < 0) y_mult = -y_mult; - FT_Set_Char_Size (ft_face, - abs (font->x_scale), abs (font->y_scale), - 0, 0); + if (FT_Set_Char_Size (ft_face, + abs (font->x_scale), abs (font->y_scale), + 0, 0 #if 0 font->x_ppem * 72 * 64 / font->x_scale, - font->y_ppem * 72 * 64 / font->y_scale); + font->y_ppem * 72 * 64 / font->y_scale #endif - if (font->x_scale < 0 || font->y_scale < 0) + ) && ft_face->num_fixed_sizes) { - FT_Matrix matrix = { font->x_scale < 0 ? -1 : +1, 0, - 0, font->y_scale < 0 ? -1 : +1}; +#ifdef HAVE_FT_GET_TRANSFORM + /* Bitmap font, eg. bitmap color emoji. */ + /* Pick largest size? */ + int x_scale = ft_face->available_sizes[ft_face->num_fixed_sizes - 1].x_ppem; + int y_scale = ft_face->available_sizes[ft_face->num_fixed_sizes - 1].y_ppem; + FT_Set_Char_Size (ft_face, + x_scale, y_scale, + 0, 0); + + /* This contains the sign that was previously in x_mult/y_mult. */ + x_mult = (float) font->x_scale / x_scale; + y_mult = (float) font->y_scale / y_scale; +#endif + } + else + { /* Shrug */ } + + + if (x_mult != 1.f || y_mult != 1.f) + { + FT_Matrix matrix = { (int) roundf (x_mult * (1<<16)), 0, + 0, (int) roundf (y_mult * (1<<16))}; FT_Set_Transform (ft_face, &matrix, nullptr); + ft_font->transform = true; } #if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR) unsigned int num_coords; - const int *coords = hb_font_get_var_coords_normalized (font, &num_coords); + const float *coords = hb_font_get_var_coords_design (font, &num_coords); if (num_coords) { FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (num_coords, sizeof (FT_Fixed)); if (ft_coords) { for (unsigned int i = 0; i < num_coords; i++) - ft_coords[i] = coords[i] * 4; - FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords); + ft_coords[i] = coords[i] * 65536.f; + FT_Set_Var_Design_Coordinates (ft_face, num_coords, ft_coords); hb_free (ft_coords); } } @@ -194,6 +227,9 @@ _hb_ft_hb_font_check_changed (hb_font_t *font, * For more information, see * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx * + * This function works with #hb_font_t objects created by + * hb_ft_font_create() or hb_ft_font_create_referenced(). + * * Since: 1.0.5 **/ void @@ -219,7 +255,10 @@ hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) * For more information, see * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx * - * Return value: FT_Load_Glyph flags found + * This function works with #hb_font_t objects created by + * hb_ft_font_create() or hb_ft_font_create_referenced(). + * + * Return value: FT_Load_Glyph flags found, or 0 * * Since: 1.0.5 **/ @@ -241,7 +280,10 @@ hb_ft_font_get_load_flags (hb_font_t *font) * Fetches the FT_Face associated with the specified #hb_font_t * font object. * - * Return value: (nullable): the FT_Face found or %NULL + * This function works with #hb_font_t objects created by + * hb_ft_font_create() or hb_ft_font_create_referenced(). + * + * Return value: (nullable): the FT_Face found or `NULL` * * Since: 0.9.2 **/ @@ -260,10 +302,15 @@ hb_ft_font_get_face (hb_font_t *font) * hb_ft_font_lock_face: (skip) * @font: #hb_font_t to work upon * - * Gets the FT_Face associated with @font, This face will be kept around until - * you call hb_ft_font_unlock_face(). + * Gets the FT_Face associated with @font. + * + * This face will be kept around and access to the FT_Face object + * from other HarfBuzz API wil be blocked until you call hb_ft_font_unlock_face(). * - * Return value: (nullable): the FT_Face associated with @font or %NULL + * This function works with #hb_font_t objects created by + * hb_ft_font_create() or hb_ft_font_create_referenced(). + * + * Return value: (nullable) (transfer none): the FT_Face associated with @font or `NULL` * Since: 2.6.5 **/ FT_Face @@ -280,7 +327,7 @@ hb_ft_font_lock_face (hb_font_t *font) } /** - * hb_ft_font_unlock_face: + * hb_ft_font_unlock_face: (skip) * @font: #hb_font_t to work upon * * Releases an FT_Face previously obtained with hb_ft_font_lock_face(). @@ -401,10 +448,24 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data, void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_position_t *orig_first_advance = first_advance; hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; int load_flags = ft_font->load_flags; - int mult = font->x_scale < 0 ? -1 : +1; + float x_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f; + x_mult *= font->x_scale < 0 ? -1 : +1; + } + else +#endif + { + x_mult = font->x_scale < 0 ? -1 : +1; + } for (unsigned int i = 0; i < count; i++) { @@ -417,13 +478,29 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data, else { FT_Get_Advance (ft_face, glyph, load_flags, &v); + /* Work around bug that FreeType seems to return negative advance + * for variable-set fonts if x_scale is negative! */ + v = abs (v); + v = (int) (v * x_mult + (1<<9)) >> 10; ft_font->advance_cache.set (glyph, v); } - *first_advance = (v * mult + (1<<9)) >> 10; + *first_advance = v; first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } + + if (font->x_strength && !font->embolden_in_place) + { + /* Emboldening. */ + hb_position_t x_strength = font->x_scale >= 0 ? font->x_strength : -font->x_strength; + first_advance = orig_first_advance; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? x_strength : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } } #ifndef HB_NO_VERTICAL @@ -436,17 +513,31 @@ hb_ft_get_glyph_v_advance (hb_font_t *font, const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; hb_lock_t lock (ft_font->lock); FT_Fixed v; + float y_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_font->ft_face, &matrix, nullptr); + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + y_mult *= font->y_scale < 0 ? -1 : +1; + } + else +#endif + { + y_mult = font->y_scale < 0 ? -1 : +1; + } if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v))) return 0; - if (font->y_scale < 0) - v = -v; + v = (int) (y_mult * v); /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates * have a Y growing upward. Hence the extra negation. */ - return (-v + (1<<9)) >> 10; + hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength; + return ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength); } #endif @@ -462,6 +553,23 @@ hb_ft_get_glyph_v_origin (hb_font_t *font, const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; + float x_mult, y_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f; + x_mult *= font->x_scale < 0 ? -1 : +1; + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + y_mult *= font->y_scale < 0 ? -1 : +1; + } + else +#endif + { + x_mult = font->x_scale < 0 ? -1 : +1; + y_mult = font->y_scale < 0 ? -1 : +1; + } if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) return false; @@ -471,10 +579,8 @@ hb_ft_get_glyph_v_origin (hb_font_t *font, *x = ft_face->glyph->metrics.horiBearingX - ft_face->glyph->metrics.vertBearingX; *y = ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY); - if (font->x_scale < 0) - *x = -*x; - if (font->y_scale < 0) - *y = -*y; + *x = (hb_position_t) (x_mult * *x); + *y = (hb_position_t) (y_mult * *y); return true; } @@ -510,24 +616,63 @@ hb_ft_get_glyph_extents (hb_font_t *font, const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; + float x_mult, y_mult; + float slant_xy = font->slant_xy; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f; + x_mult *= font->x_scale < 0 ? -1 : +1; + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + y_mult *= font->y_scale < 0 ? -1 : +1; + } + else +#endif + { + x_mult = font->x_scale < 0 ? -1 : +1; + y_mult = font->y_scale < 0 ? -1 : +1; + } if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) return false; - extents->x_bearing = ft_face->glyph->metrics.horiBearingX; - extents->y_bearing = ft_face->glyph->metrics.horiBearingY; - extents->width = ft_face->glyph->metrics.width; - extents->height = -ft_face->glyph->metrics.height; - if (font->x_scale < 0) + /* Copied from hb_font_t::scale_glyph_extents. */ + + float x1 = x_mult * ft_face->glyph->metrics.horiBearingX; + float y1 = y_mult * ft_face->glyph->metrics.horiBearingY; + float x2 = x1 + x_mult * ft_face->glyph->metrics.width; + float y2 = y1 + y_mult * -ft_face->glyph->metrics.height; + + /* Apply slant. */ + if (slant_xy) { - extents->x_bearing = -extents->x_bearing; - extents->width = -extents->width; + x1 += hb_min (y1 * slant_xy, y2 * slant_xy); + x2 += hb_max (y1 * slant_xy, y2 * slant_xy); } - if (font->y_scale < 0) + + extents->x_bearing = floorf (x1); + extents->y_bearing = floorf (y1); + extents->width = ceilf (x2) - extents->x_bearing; + extents->height = ceilf (y2) - extents->y_bearing; + + if (font->x_strength || font->y_strength) { - extents->y_bearing = -extents->y_bearing; - extents->height = -extents->height; + /* Y */ + int y_shift = font->y_strength; + if (font->y_scale < 0) y_shift = -y_shift; + extents->y_bearing += y_shift; + extents->height -= y_shift; + + /* X */ + int x_shift = font->x_strength; + if (font->x_scale < 0) x_shift = -x_shift; + if (font->embolden_in_place) + extents->x_bearing -= x_shift / 2; + extents->width += x_shift; } + return true; } @@ -620,16 +765,39 @@ hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; + float y_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + y_mult *= font->y_scale < 0 ? -1 : +1; + } + else +#endif + { + y_mult = font->y_scale < 0 ? -1 : +1; + } - metrics->ascender = FT_MulFix(ft_face->ascender, ft_face->size->metrics.y_scale); - metrics->descender = FT_MulFix(ft_face->descender, ft_face->size->metrics.y_scale); - metrics->line_gap = FT_MulFix( ft_face->height, ft_face->size->metrics.y_scale ) - (metrics->ascender - metrics->descender); - if (font->y_scale < 0) + if (ft_face->units_per_EM != 0) { - metrics->ascender = -metrics->ascender; - metrics->descender = -metrics->descender; - metrics->line_gap = -metrics->line_gap; + metrics->ascender = FT_MulFix(ft_face->ascender, ft_face->size->metrics.y_scale); + metrics->descender = FT_MulFix(ft_face->descender, ft_face->size->metrics.y_scale); + metrics->line_gap = FT_MulFix( ft_face->height, ft_face->size->metrics.y_scale ) - (metrics->ascender - metrics->descender); } + else + { + /* Bitmap-only font, eg. color bitmap font. */ + metrics->ascender = ft_face->size->metrics.ascender; + metrics->descender = ft_face->size->metrics.descender; + metrics->line_gap = ft_face->size->metrics.height - (metrics->ascender - metrics->descender); + } + + metrics->ascender = (hb_position_t) (y_mult * (metrics->ascender + font->y_strength)); + metrics->descender = (hb_position_t) (y_mult * metrics->descender); + metrics->line_gap = (hb_position_t) (y_mult * metrics->line_gap); + return true; } @@ -637,16 +805,18 @@ hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, static int _hb_ft_move_to (const FT_Vector *to, - hb_draw_session_t *drawing) + void *arg) { + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; drawing->move_to (to->x, to->y); return FT_Err_Ok; } static int _hb_ft_line_to (const FT_Vector *to, - hb_draw_session_t *drawing) + void *arg) { + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; drawing->line_to (to->x, to->y); return FT_Err_Ok; } @@ -654,8 +824,9 @@ _hb_ft_line_to (const FT_Vector *to, static int _hb_ft_conic_to (const FT_Vector *control, const FT_Vector *to, - hb_draw_session_t *drawing) + void *arg) { + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; drawing->quadratic_to (control->x, control->y, to->x, to->y); return FT_Err_Ok; @@ -665,8 +836,9 @@ static int _hb_ft_cubic_to (const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, - hb_draw_session_t *drawing) + void *arg) { + hb_draw_session_t *drawing = (hb_draw_session_t *) arg; drawing->cubic_to (control1->x, control1->y, control2->x, control2->y, to->x, to->y); @@ -674,18 +846,16 @@ _hb_ft_cubic_to (const FT_Vector *control1, } static void -hb_ft_get_glyph_shape (hb_font_t *font HB_UNUSED, - void *font_data, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, void *draw_data, - void *user_data HB_UNUSED) +hb_ft_draw_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data HB_UNUSED) { const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; hb_lock_t lock (ft_font->lock); FT_Face ft_face = ft_font->ft_face; - _hb_ft_hb_font_check_changed (font, ft_font); - if (unlikely (FT_Load_Glyph (ft_face, glyph, FT_LOAD_NO_BITMAP | ft_font->load_flags))) return; @@ -694,22 +864,139 @@ hb_ft_get_glyph_shape (hb_font_t *font HB_UNUSED, return; const FT_Outline_Funcs outline_funcs = { - (FT_Outline_MoveToFunc) _hb_ft_move_to, - (FT_Outline_LineToFunc) _hb_ft_line_to, - (FT_Outline_ConicToFunc) _hb_ft_conic_to, - (FT_Outline_CubicToFunc) _hb_ft_cubic_to, + _hb_ft_move_to, + _hb_ft_line_to, + _hb_ft_conic_to, + _hb_ft_cubic_to, 0, /* shift */ 0, /* delta */ }; hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy); + /* Embolden */ + if (font->x_strength || font->y_strength) + { + FT_Outline_EmboldenXY (&ft_face->glyph->outline, font->x_strength, font->y_strength); + + int x_shift = 0; + int y_shift = 0; + if (font->embolden_in_place) + { + /* Undo the FreeType shift. */ + x_shift = -font->x_strength / 2; + y_shift = 0; + if (font->y_scale < 0) y_shift = -font->y_strength; + } + else + { + /* FreeType applied things in the wrong direction for negative scale; fix up. */ + if (font->x_scale < 0) x_shift = -font->x_strength; + if (font->y_scale < 0) y_shift = -font->y_strength; + } + if (x_shift || y_shift) + { + auto &outline = ft_face->glyph->outline; + for (auto &point : hb_iter (outline.points, outline.contours[outline.n_contours - 1] + 1)) + { + point.x += x_shift; + point.y += y_shift; + } + } + } + + FT_Outline_Decompose (&ft_face->glyph->outline, &outline_funcs, &draw_session); } #endif +#ifndef HB_NO_PAINT +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21300 + +#include "hb-ft-colr.hh" + +static void +hb_ft_paint_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t gid, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette_index, + hb_color_t foreground, + void *user_data) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + /* We release the lock before calling into glyph callbacks, such that + * eg. draw API can call back into the face.*/ + + if (unlikely (FT_Load_Glyph (ft_face, gid, + ft_font->load_flags | FT_LOAD_COLOR))) + return; + + if (ft_face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) + { + if (hb_ft_paint_glyph_colr (font, font_data, gid, + paint_funcs, paint_data, + palette_index, foreground, + user_data)) + return; + + /* Simple outline. */ + ft_font->lock.unlock (); + paint_funcs->push_clip_glyph (paint_data, gid, font); + ft_font->lock.lock (); + paint_funcs->color (paint_data, true, foreground); + paint_funcs->pop_clip (paint_data); + + return; + } + + auto *glyph = ft_face->glyph; + if (glyph->format == FT_GLYPH_FORMAT_BITMAP) + { + auto &bitmap = glyph->bitmap; + if (bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) + { + if (bitmap.pitch != (signed) bitmap.width * 4) + return; + + ft_font->lock.unlock (); + + hb_blob_t *blob = hb_blob_create ((const char *) bitmap.buffer, + bitmap.pitch * bitmap.rows, + HB_MEMORY_MODE_DUPLICATE, + nullptr, nullptr); + + hb_glyph_extents_t extents; + if (!hb_font_get_glyph_extents (font, gid, &extents)) + goto out; + + if (!paint_funcs->image (paint_data, + blob, + bitmap.width, + bitmap.rows, + HB_PAINT_IMAGE_FORMAT_BGRA, + font->slant_xy, + &extents)) + { + /* TODO Try a forced outline load and paint? */ + } + + out: + hb_blob_destroy (blob); + ft_font->lock.lock (); + } + + return; + } +} +#endif +#endif + static inline void free_static_ft_funcs (); @@ -743,7 +1030,13 @@ static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t= 21300 + hb_font_funcs_set_paint_glyph_func (funcs, hb_ft_paint_glyph, nullptr, nullptr); +#endif #endif hb_font_funcs_make_immutable (funcs); @@ -818,6 +1111,10 @@ _hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data * * Creates an #hb_face_t face object from the specified FT_Face. * + * Note that this is using the FT_Face object just to get at the underlying + * font data, and fonts created from the returned #hb_face_t will use the native + * HarfBuzz font implementation, unless you call hb_ft_font_set_funcs() on them. + * * This variant of the function does not provide any life-cycle management. * * Most client programs should use hb_ft_face_create_referenced() @@ -862,6 +1159,10 @@ hb_ft_face_create (FT_Face ft_face, * * Creates an #hb_face_t face object from the specified FT_Face. * + * Note that this is using the FT_Face object just to get at the underlying + * font data, and fonts created from the returned #hb_face_t will use the native + * HarfBuzz font implementation, unless you call hb_ft_font_set_funcs() on them. + * * This is the preferred variant of the hb_ft_face_create* * function family, because it calls FT_Reference_Face() on @ft_face, * ensuring that @ft_face remains alive as long as the resulting @@ -882,8 +1183,9 @@ hb_ft_face_create_referenced (FT_Face ft_face) } static void -hb_ft_face_finalize (FT_Face ft_face) +hb_ft_face_finalize (void *arg) { + FT_Face ft_face = (FT_Face) arg; hb_face_destroy ((hb_face_t *) ft_face->generic.data); } @@ -893,6 +1195,10 @@ hb_ft_face_finalize (FT_Face ft_face) * * Creates an #hb_face_t face object from the specified FT_Face. * + * Note that this is using the FT_Face object just to get at the underlying + * font data, and fonts created from the returned #hb_face_t will use the native + * HarfBuzz font implementation, unless you call hb_ft_font_set_funcs() on them. + * * This variant of the function caches the newly created #hb_face_t * face object, using the @generic pointer of @ft_face. Subsequent function * calls that are passed the same @ft_face parameter will have the same @@ -915,7 +1221,7 @@ hb_ft_face_create_cached (FT_Face ft_face) ft_face->generic.finalizer (ft_face); ft_face->generic.data = hb_ft_face_create (ft_face, nullptr); - ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize; + ft_face->generic.finalizer = hb_ft_face_finalize; } return hb_face_reference ((hb_face_t *) ft_face->generic.data); @@ -1028,6 +1334,9 @@ hb_ft_font_changed (hb_font_t *font) #endif } #endif + + ft_font->advance_cache.clear (); + ft_font->cached_serial = font->serial; } /** @@ -1121,8 +1430,9 @@ get_ft_library () } static void -_release_blob (FT_Face ft_face) +_release_blob (void *arg) { + FT_Face ft_face = (FT_Face) arg; hb_blob_destroy ((hb_blob_t *) ft_face->generic.data); } @@ -1139,10 +1449,14 @@ _release_blob (FT_Face ft_face) * created with hb_face_create(), and therefore was not * initially configured to use FreeType font functions. * - * An #hb_face_t face object created with hb_ft_face_create() + * An #hb_font_t object created with hb_ft_font_create() * is preconfigured for FreeType font functions and does not * require this function to be used. * + * Note that if you modify the underlying #hb_font_t after + * calling this function, you need to call hb_ft_hb_font_changed() + * to update the underlying FT_Face. + * * Note: Internally, this function creates an FT_Face. * * @@ -1173,14 +1487,14 @@ hb_ft_font_set_funcs (hb_font_t *font) if (FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL)) FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE); - _hb_ft_hb_font_changed (font, ft_face); ft_face->generic.data = blob; - ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob; + ft_face->generic.finalizer = _release_blob; _hb_ft_font_set_funcs (font, ft_face, true); hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); -} + _hb_ft_hb_font_changed (font, ft_face); +} #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-iter.hh b/src/java.desktop/share/native/libharfbuzz/hb-iter.hh index ff7a4c75895..bcd4eb8ebc4 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-iter.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-iter.hh @@ -73,8 +73,10 @@ struct hb_iter_t /* Operators. */ iter_t iter () const { return *thiz(); } iter_t operator + () const { return *thiz(); } - iter_t begin () const { return *thiz(); } - iter_t end () const { return thiz()->__end__ (); } + iter_t _begin () const { return *thiz(); } + iter_t begin () const { return _begin (); } + iter_t _end () const { return thiz()->__end__ (); } + iter_t end () const { return _end (); } explicit operator bool () const { return thiz()->__more__ (); } unsigned len () const { return thiz()->__len__ (); } /* The following can only be enabled if item_t is reference type. Otherwise @@ -118,7 +120,9 @@ struct hb_iter_t #define HB_ITER_USING(Name) \ using item_t = typename Name::item_t; \ + using Name::_begin; \ using Name::begin; \ + using Name::_end; \ using Name::end; \ using Name::get_item_size; \ using Name::is_iterator; \ @@ -168,10 +172,16 @@ struct HB_FUNCOBJ (hb_iter); struct { - template unsigned - operator () (T&& c) const - { return c.len (); } + template auto + impl (T&& c, hb_priority<1>) const HB_RETURN (unsigned, c.len ()) + + template auto + impl (T&& c, hb_priority<0>) const HB_RETURN (unsigned, c.len) + + public: + template auto + operator () (T&& c) const HB_RETURN (unsigned, impl (std::forward (c), hb_prioritize)) } HB_FUNCOBJ (hb_len); @@ -253,6 +263,8 @@ struct hb_is_iterator_of }; #define hb_is_iterator_of(Iter, Item) hb_is_iterator_of::value #define hb_is_iterator(Iter) hb_is_iterator_of (Iter, typename Iter::item_t) +#define hb_is_sorted_iterator_of(Iter, Item) (hb_is_iterator_of::value && Iter::is_sorted_iterator) +#define hb_is_sorted_iterator(Iter) hb_is_sorted_iterator_of (Iter, typename Iter::item_t) /* hb_is_iterable() */ @@ -375,7 +387,7 @@ struct hb_map_iter_t : void __forward__ (unsigned n) { it += n; } void __prev__ () { --it; } void __rewind__ (unsigned n) { it -= n; } - hb_map_iter_t __end__ () const { return hb_map_iter_t (it.end (), f); } + hb_map_iter_t __end__ () const { return hb_map_iter_t (it._end (), f); } bool operator != (const hb_map_iter_t& o) const { return it != o.it; } @@ -438,7 +450,7 @@ struct hb_filter_iter_t : bool __more__ () const { return bool (it); } void __next__ () { do ++it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } void __prev__ () { do --it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } - hb_filter_iter_t __end__ () const { return hb_filter_iter_t (it.end (), p, f); } + hb_filter_iter_t __end__ () const { return hb_filter_iter_t (it._end (), p, f); } bool operator != (const hb_filter_iter_t& o) const { return it != o.it; } @@ -551,7 +563,7 @@ struct hb_zip_iter_t : void __forward__ (unsigned n) { a += n; b += n; } void __prev__ () { --a; --b; } void __rewind__ (unsigned n) { a -= n; b -= n; } - hb_zip_iter_t __end__ () const { return hb_zip_iter_t (a.end (), b.end ()); } + hb_zip_iter_t __end__ () const { return hb_zip_iter_t (a._end (), b._end ()); } /* Note, we should stop if ANY of the iters reaches end. As such two compare * unequal if both items are unequal, NOT if either is unequal. */ bool operator != (const hb_zip_iter_t& o) const @@ -635,7 +647,7 @@ struct hb_concat_iter_t : } } - hb_concat_iter_t __end__ () const { return hb_concat_iter_t (a.end (), b.end ()); } + hb_concat_iter_t __end__ () const { return hb_concat_iter_t (a._end (), b._end ()); } bool operator != (const hb_concat_iter_t& o) const { return a != o.a diff --git a/src/java.desktop/share/native/libharfbuzz/hb-limits.hh b/src/java.desktop/share/native/libharfbuzz/hb-limits.hh new file mode 100644 index 00000000000..0f60e9e2101 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-limits.hh @@ -0,0 +1,109 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_LIMITS_HH +#define HB_LIMITS_HH + +#include "hb.hh" + + +#ifndef HB_BUFFER_MAX_LEN_FACTOR +#define HB_BUFFER_MAX_LEN_FACTOR 64 +#endif +#ifndef HB_BUFFER_MAX_LEN_MIN +#define HB_BUFFER_MAX_LEN_MIN 16384 +#endif +#ifndef HB_BUFFER_MAX_LEN_DEFAULT +#define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */ +#endif + +#ifndef HB_BUFFER_MAX_OPS_FACTOR +#define HB_BUFFER_MAX_OPS_FACTOR 1024 +#endif +#ifndef HB_BUFFER_MAX_OPS_MIN +#define HB_BUFFER_MAX_OPS_MIN 16384 +#endif +#ifndef HB_BUFFER_MAX_OPS_DEFAULT +#define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */ +#endif + + +#ifndef HB_MAX_NESTING_LEVEL +#define HB_MAX_NESTING_LEVEL 64 +#endif + + +#ifndef HB_MAX_CONTEXT_LENGTH +#define HB_MAX_CONTEXT_LENGTH 64 +#endif + +#ifndef HB_CLOSURE_MAX_STAGES +/* + * The maximum number of times a lookup can be applied during shaping. + * Used to limit the number of iterations of the closure algorithm. + * This must be larger than the number of times add_gsub_pause() is + * called in a collect_features call of any shaper. + */ +#define HB_CLOSURE_MAX_STAGES 12 +#endif + +#ifndef HB_MAX_SCRIPTS +#define HB_MAX_SCRIPTS 500 +#endif + +#ifndef HB_MAX_LANGSYS +#define HB_MAX_LANGSYS 2000 +#endif + +#ifndef HB_MAX_LANGSYS_FEATURE_COUNT +#define HB_MAX_LANGSYS_FEATURE_COUNT 50000 +#endif + +#ifndef HB_MAX_FEATURE_INDICES +#define HB_MAX_FEATURE_INDICES 1500 +#endif + +#ifndef HB_MAX_LOOKUP_VISIT_COUNT +#define HB_MAX_LOOKUP_VISIT_COUNT 35000 +#endif + + +#ifndef HB_GLYF_MAX_POINTS +#define HB_GLYF_MAX_POINTS 20000 +#endif + +#ifndef HB_GLYF_MAX_EDGE_COUNT +#define HB_GLYF_MAX_EDGE_COUNT 1024 +#endif + +#ifndef HB_CFF_MAX_OPS +#define HB_CFF_MAX_OPS 10000 +#endif + +#ifndef HB_COLRV1_MAX_EDGE_COUNT +#define HB_COLRV1_MAX_EDGE_COUNT 1024 +#endif + + +#endif /* HB_LIMITS_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh b/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh index 5938a25504a..3a048fc1242 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh @@ -34,7 +34,6 @@ #include "hb-dispatch.hh" #include "hb-sanitize.hh" -#include "hb-serialize.hh" /* @@ -136,6 +135,13 @@ static inline Type& StructAfter(TObject &X) /* * Lazy loaders. + * + * The lazy-loaders are thread-safe pointer-like objects that create their + * instead on-demand. They also support access to a "data" object that is + * necessary for creating their instance. The data object, if specified, + * is accessed via pointer math, located at a location before the position + * of the loader itself. This avoids having to store a pointer to data + * for every lazy-loader. Multiple lazy-loaders can access the same data. */ template @@ -176,12 +182,12 @@ struct hb_lazy_loader_t : hb_data_wrapper_t void init0 () {} /* Init, when memory is already set to 0. No-op for us. */ void init () { instance.set_relaxed (nullptr); } - void fini () { do_destroy (instance.get ()); init (); } + void fini () { do_destroy (instance.get_acquire ()); init (); } void free_instance () { retry: - Stored *p = instance.get (); + Stored *p = instance.get_acquire (); if (unlikely (p && !cmpexch (p, nullptr))) goto retry; do_destroy (p); @@ -203,7 +209,7 @@ struct hb_lazy_loader_t : hb_data_wrapper_t Stored * get_stored () const { retry: - Stored *p = this->instance.get (); + Stored *p = this->instance.get_acquire (); if (unlikely (!p)) { if (unlikely (this->is_inert ())) @@ -228,7 +234,8 @@ struct hb_lazy_loader_t : hb_data_wrapper_t bool cmpexch (Stored *current, Stored *value) const { - /* This *must* be called when there are no other threads accessing. */ + /* This function can only be safely called directly if no + * other thread is accessing. */ return this->instance.cmpexch (current, value); } @@ -261,7 +268,7 @@ struct hb_lazy_loader_t : hb_data_wrapper_t hb_free (p); } -// private: + private: /* Must only have one pointer. */ hb_atomic_ptr_t instance; }; @@ -283,7 +290,7 @@ struct hb_table_lazy_loader_t : hb_lazy_loader_t (face); } static void destroy (hb_blob_t *p) { hb_blob_destroy (p); } @@ -297,22 +304,22 @@ struct hb_table_lazy_loader_t : hb_lazy_loader_tget_stored (); } }; -template -struct hb_font_funcs_lazy_loader_t : hb_lazy_loader_t -{ - static void destroy (hb_font_funcs_t *p) - { hb_font_funcs_destroy (p); } - static const hb_font_funcs_t *get_null () - { return hb_font_funcs_get_empty (); } -}; -template -struct hb_unicode_funcs_lazy_loader_t : hb_lazy_loader_t -{ - static void destroy (hb_unicode_funcs_t *p) - { hb_unicode_funcs_destroy (p); } - static const hb_unicode_funcs_t *get_null () - { return hb_unicode_funcs_get_empty (); } -}; +#define HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T(Type) \ + template \ + struct hb_##Type##_funcs_lazy_loader_t : hb_lazy_loader_t \ + { \ + static void destroy (hb_##Type##_funcs_t *p) \ + { hb_##Type##_funcs_destroy (p); } \ + static const hb_##Type##_funcs_t *get_null () \ + { return hb_##Type##_funcs_get_empty (); } \ + } + +HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (font); +HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (unicode); +HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (draw); +HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T (paint); + +#undef HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T #endif /* HB_MACHINERY_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-map.cc b/src/java.desktop/share/native/libharfbuzz/hb-map.cc index af449bea411..863a43aa704 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-map.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-map.cc @@ -56,8 +56,6 @@ hb_map_create () if (!(map = hb_object_create ())) return hb_map_get_empty (); - map->init_shallow (); - return map; } @@ -107,8 +105,6 @@ hb_map_destroy (hb_map_t *map) { if (!hb_object_destroy (map)) return; - map->fini_shallow (); - hb_free (map); } @@ -122,7 +118,7 @@ hb_map_destroy (hb_map_t *map) * * Attaches a user-data key/data pair to the specified map. * - * Return value: %true if success, %false otherwise + * Return value: `true` if success, `false` otherwise * * Since: 1.7.7 **/ @@ -149,7 +145,7 @@ hb_map_set_user_data (hb_map_t *map, * Since: 1.7.7 **/ void * -hb_map_get_user_data (hb_map_t *map, +hb_map_get_user_data (const hb_map_t *map, hb_user_data_key_t *key) { return hb_object_get_user_data (map, key); @@ -162,7 +158,7 @@ hb_map_get_user_data (hb_map_t *map, * * Tests whether memory allocation for a set was successful. * - * Return value: %true if allocation succeeded, %false otherwise + * Return value: `true` if allocation succeeded, `false` otherwise * * Since: 1.7.7 **/ @@ -178,7 +174,7 @@ hb_map_allocation_successful (const hb_map_t *map) * * Allocate a copy of @map. * - * Return value: Newly-allocated map. + * Return value: (transfer full): Newly-allocated map. * * Since: 4.4.0 **/ @@ -186,9 +182,10 @@ hb_map_t * hb_map_copy (const hb_map_t *map) { hb_map_t *copy = hb_map_create (); - if (unlikely (!copy)) return nullptr; - copy->resize (map->population); - hb_copy (*map, *copy); + if (unlikely (copy->in_error ())) + return hb_map_get_empty (); + + *copy = *map; return copy; } @@ -251,7 +248,7 @@ hb_map_del (hb_map_t *map, * * Tests whether @key is an element of @map. * - * Return value: %true if @key is found in @map, %false otherwise + * Return value: `true` if @key is found in @map, `false` otherwise * * Since: 1.7.7 **/ @@ -283,7 +280,7 @@ hb_map_clear (hb_map_t *map) * * Tests whether @map is empty (contains no elements). * - * Return value: %true if @map is empty + * Return value: `true` if @map is empty * * Since: 1.7.7 **/ @@ -317,7 +314,7 @@ hb_map_get_population (const hb_map_t *map) * Tests whether @map and @other are equal (contain the same * elements). * - * Return value: %true if the two maps are equal, %false otherwise. + * Return value: `true` if the two maps are equal, `false` otherwise. * * Since: 4.3.0 **/ @@ -339,9 +336,84 @@ hb_map_is_equal (const hb_map_t *map, * * Since: 4.4.0 **/ -HB_EXTERN unsigned int +unsigned int hb_map_hash (const hb_map_t *map) { return map->hash (); } +/** + * hb_map_update: + * @map: A map + * @other: Another map + * + * Add the contents of @other to @map. + * + * Since: 7.0.0 + **/ +HB_EXTERN void +hb_map_update (hb_map_t *map, + const hb_map_t *other) +{ + map->update (*other); +} + +/** + * hb_map_next: + * @map: A map + * @idx: (inout): Iterator internal state + * @key: (out): Key retrieved + * @value: (out): Value retrieved + * + * Fetches the next key/value paire in @map. + * + * Set @idx to -1 to get started. + * + * If the map is modified during iteration, the behavior is undefined. + * + * The order in which the key/values are returned is undefined. + * + * Return value: `true` if there was a next value, `false` otherwise + * + * Since: 7.0.0 + **/ +hb_bool_t +hb_map_next (const hb_map_t *map, + int *idx, + hb_codepoint_t *key, + hb_codepoint_t *value) +{ + return map->next (idx, key, value); +} + +/** + * hb_map_keys: + * @map: A map + * @keys: A set + * + * Add the keys of @map to @keys. + * + * Since: 7.0.0 + **/ +void +hb_map_keys (const hb_map_t *map, + hb_set_t *keys) +{ + map->keys (*keys); +} + +/** + * hb_map_values: + * @map: A map + * @values: A set + * + * Add the values of @map to @values. + * + * Since: 7.0.0 + **/ +void +hb_map_values (const hb_map_t *map, + hb_set_t *values) +{ + map->values (*values); +} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-map.h b/src/java.desktop/share/native/libharfbuzz/hb-map.h index 27c19c2b589..12d8970f48f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-map.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-map.h @@ -32,6 +32,7 @@ #define HB_MAP_H #include "hb-common.h" +#include "hb-set.h" HB_BEGIN_DECLS @@ -74,7 +75,7 @@ hb_map_set_user_data (hb_map_t *map, hb_bool_t replace); HB_EXTERN void * -hb_map_get_user_data (hb_map_t *map, +hb_map_get_user_data (const hb_map_t *map, hb_user_data_key_t *key); @@ -118,6 +119,24 @@ HB_EXTERN hb_bool_t hb_map_has (const hb_map_t *map, hb_codepoint_t key); +HB_EXTERN void +hb_map_update (hb_map_t *map, + const hb_map_t *other); + +/* Pass -1 in for idx to get started. */ +HB_EXTERN hb_bool_t +hb_map_next (const hb_map_t *map, + int *idx, + hb_codepoint_t *key, + hb_codepoint_t *value); + +HB_EXTERN void +hb_map_keys (const hb_map_t *map, + hb_set_t *keys); + +HB_EXTERN void +hb_map_values (const hb_map_t *map, + hb_set_t *values); HB_END_DECLS diff --git a/src/java.desktop/share/native/libharfbuzz/hb-map.hh b/src/java.desktop/share/native/libharfbuzz/hb-map.hh index ff5a476d7a2..5c351e2c8a0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-map.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-map.hh @@ -29,6 +29,8 @@ #include "hb.hh" +#include "hb-set.hh" + /* * hb_hashmap_t @@ -43,9 +45,9 @@ struct hb_hashmap_t hb_hashmap_t () { init (); } ~hb_hashmap_t () { fini (); } - hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { resize (population); hb_copy (o, *this); } + hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { resize (o.population); hb_copy (o, *this); } hb_hashmap_t (hb_hashmap_t&& o) : hb_hashmap_t () { hb_swap (*this, o); } - hb_hashmap_t& operator= (const hb_hashmap_t& o) { resize (population); hb_copy (o, *this); return *this; } + hb_hashmap_t& operator= (const hb_hashmap_t& o) { reset (); resize (o.population); hb_copy (o, *this); return *this; } hb_hashmap_t& operator= (hb_hashmap_t&& o) { hb_swap (*this, o); return *this; } hb_hashmap_t (std::initializer_list> lst) : hb_hashmap_t () @@ -71,6 +73,11 @@ struct hb_hashmap_t uint32_t is_tombstone_ : 1; V value; + item_t () : key (), + hash (0), + is_used_ (false), is_tombstone_ (false), + value () {} + bool is_used () const { return is_used_; } void set_used (bool is_used) { is_used_ = is_used; } bool is_tombstone () const { return is_tombstone_; } @@ -88,17 +95,8 @@ struct hb_hashmap_t return minus_1; }; - void clear () - { - new (std::addressof (key)) K (); - new (std::addressof (value)) V (); - hash = 0; - is_used_ = false; - is_tombstone_ = false; - } - - bool operator == (const K &o) { return hb_deref (key) == hb_deref (o); } - bool operator == (const item_t &o) { return *this == o.key; } + bool operator == (const K &o) const { return hb_deref (key) == hb_deref (o); } + bool operator == (const item_t &o) const { return *this == o.key; } hb_pair_t get_pair() const { return hb_pair_t (key, value); } hb_pair_t get_pair_ref() const { return hb_pair_t (key, value); } @@ -107,8 +105,8 @@ struct hb_hashmap_t }; hb_object_header_t header; - bool successful; /* Allocations successful */ - unsigned int population; /* Not including tombstones. */ + unsigned int successful : 1; /* Allocations successful */ + unsigned int population : 31; /* Not including tombstones. */ unsigned int occupancy; /* Including tombstones. */ unsigned int mask; unsigned int prime; @@ -118,27 +116,29 @@ struct hb_hashmap_t { if (unlikely (!a.successful || !b.successful)) return; - hb_swap (a.population, b.population); + unsigned tmp = a.population; + a.population = b.population; + b.population = tmp; + //hb_swap (a.population, b.population); hb_swap (a.occupancy, b.occupancy); hb_swap (a.mask, b.mask); hb_swap (a.prime, b.prime); hb_swap (a.items, b.items); } - void init_shallow () + void init () { + hb_object_init (this); + successful = true; population = occupancy = 0; mask = 0; prime = 0; items = nullptr; } - void init () - { - hb_object_init (this); - init_shallow (); - } - void fini_shallow () + void fini () { + hb_object_fini (this); + if (likely (items)) { unsigned size = mask + 1; for (unsigned i = 0; i < size; i++) @@ -148,11 +148,6 @@ struct hb_hashmap_t } population = occupancy = 0; } - void fini () - { - hb_object_fini (this); - fini_shallow (); - } void reset () { @@ -166,7 +161,9 @@ struct hb_hashmap_t { if (unlikely (!successful)) return false; - unsigned int power = hb_bit_storage (hb_max (population, new_population) * 2 + 8); + if (new_population != 0 && (new_population + new_population / 2) < mask) return true; + + unsigned int power = hb_bit_storage (hb_max ((unsigned) population, new_population) * 2 + 8); unsigned int new_size = 1u << power; item_t *new_items = (item_t *) hb_malloc ((size_t) new_size * sizeof (item_t)); if (unlikely (!new_items)) @@ -175,9 +172,9 @@ struct hb_hashmap_t return false; } for (auto &_ : hb_iter (new_items, new_size)) - _.clear (); + new (&_) item_t (); - unsigned int old_size = mask + 1; + unsigned int old_size = size (); item_t *old_items = items; /* Switch to new, empty, array. */ @@ -187,67 +184,102 @@ struct hb_hashmap_t items = new_items; /* Insert back old items. */ - if (old_items) - for (unsigned int i = 0; i < old_size; i++) + for (unsigned int i = 0; i < old_size; i++) + { + if (old_items[i].is_real ()) { - if (old_items[i].is_real ()) - { - set_with_hash (old_items[i].key, - old_items[i].hash, - std::move (old_items[i].value)); - } - old_items[i].~item_t (); + set_with_hash (std::move (old_items[i].key), + old_items[i].hash, + std::move (old_items[i].value)); } + old_items[i].~item_t (); + } hb_free (old_items); return true; } + template + bool set_with_hash (KK&& key, uint32_t hash, VV&& value, bool is_delete=false) + { + if (unlikely (!successful)) return false; + if (unlikely ((occupancy + occupancy / 2) >= mask && !resize ())) return false; + item_t &item = item_for_hash (key, hash); + + if (is_delete && !(item == key)) + return true; /* Trying to delete non-existent key. */ + + if (item.is_used ()) + { + occupancy--; + if (!item.is_tombstone ()) + population--; + } + + item.key = std::forward (key); + item.value = std::forward (value); + item.hash = hash; + item.set_used (true); + item.set_tombstone (is_delete); + + occupancy++; + if (!is_delete) + population++; + + return true; + } + template - bool set (K key, VV&& value) { return set_with_hash (key, hb_hash (key), std::forward (value)); } + bool set (const K &key, VV&& value) { return set_with_hash (key, hb_hash (key), std::forward (value)); } + template + bool set (K &&key, VV&& value) { return set_with_hash (std::move (key), hb_hash (key), std::forward (value)); } - const V& get (K key) const + const V& get_with_hash (const K &key, uint32_t hash) const { if (unlikely (!items)) return item_t::default_value (); - unsigned int i = bucket_for (key); - return items[i].is_real () && items[i] == key ? items[i].value : item_t::default_value (); + auto &item = item_for_hash (key, hash); + return item.is_real () && item == key ? item.value : item_t::default_value (); + } + const V& get (const K &key) const + { + if (unlikely (!items)) return item_t::default_value (); + return get_with_hash (key, hb_hash (key)); } - void del (K key) { set_with_hash (key, hb_hash (key), item_t::default_value (), true); } + void del (const K &key) { set_with_hash (key, hb_hash (key), item_t::default_value (), true); } /* Has interface. */ - typedef const V& value_t; - value_t operator [] (K k) const { return get (k); } - bool has (K key, const V **vp = nullptr) const + const V& operator [] (K k) const { return get (k); } + template + bool has (K key, VV **vp = nullptr) const { if (unlikely (!items)) - { - if (vp) *vp = &item_t::default_value (); return false; - } - unsigned int i = bucket_for (key); - if (items[i].is_real () && items[i] == key) + auto &item = item_for_hash (key, hb_hash (key)); + if (item.is_real () && item == key) { - if (vp) *vp = &items[i].value; + if (vp) *vp = std::addressof (item.value); return true; } else - { - if (vp) *vp = &item_t::default_value (); return false; - } } /* Projection. */ V operator () (K k) const { return get (k); } + unsigned size () const { return mask ? mask + 1 : 0; } + void clear () { if (unlikely (!successful)) return; - if (items) - for (auto &_ : hb_iter (items, mask + 1)) - _.clear (); + for (auto &_ : hb_iter (items, size ())) + { + /* Reconstruct items. */ + _.~item_t (); + new (&_) item_t (); + } population = occupancy = 0; } @@ -257,11 +289,10 @@ struct hb_hashmap_t uint32_t hash () const { - uint32_t h = 0; - for (const auto &item : + hb_array (items, mask ? mask + 1 : 0) - | hb_filter (&item_t::is_real)) - h ^= item.total_hash (); - return h; + return + + iter_items () + | hb_reduce ([] (uint32_t h, const item_t &_) { return h ^ _.total_hash (); }, (uint32_t) 0u) + ; } bool is_equal (const hb_hashmap_t &other) const @@ -269,7 +300,7 @@ struct hb_hashmap_t if (population != other.population) return false; for (auto pair : iter ()) - if (get (pair.first) != pair.second) + if (other.get (pair.first) != pair.second) return false; return true; @@ -279,78 +310,98 @@ struct hb_hashmap_t unsigned int get_population () const { return population; } + void update (const hb_hashmap_t &other) + { + if (unlikely (!successful)) return; + + hb_copy (other, *this); + } + + void keys (hb_set_t &keys_) const + { + hb_copy (keys() , keys_); + } + + void values (hb_set_t &values_) const + { + hb_copy (values() , values_); + } + /* * Iterator */ - auto iter () const HB_AUTO_RETURN + + auto iter_items () const HB_AUTO_RETURN ( - + hb_array (items, mask ? mask + 1 : 0) + + hb_iter (items, size ()) | hb_filter (&item_t::is_real) - | hb_map (&item_t::get_pair) ) auto iter_ref () const HB_AUTO_RETURN ( - + hb_array (items, mask ? mask + 1 : 0) - | hb_filter (&item_t::is_real) + + iter_items () | hb_map (&item_t::get_pair_ref) ) - auto keys () const HB_AUTO_RETURN + auto iter () const HB_AUTO_RETURN ( - + hb_array (items, mask ? mask + 1 : 0) - | hb_filter (&item_t::is_real) + + iter_items () + | hb_map (&item_t::get_pair) + ) + auto keys_ref () const HB_AUTO_RETURN + ( + + iter_items () | hb_map (&item_t::key) + ) + auto keys () const HB_AUTO_RETURN + ( + + keys_ref () | hb_map (hb_ridentity) ) - auto values () const HB_AUTO_RETURN + auto values_ref () const HB_AUTO_RETURN ( - + hb_array (items, mask ? mask + 1 : 0) - | hb_filter (&item_t::is_real) + + iter_items () | hb_map (&item_t::value) + ) + auto values () const HB_AUTO_RETURN + ( + + values_ref () | hb_map (hb_ridentity) ) - /* Sink interface. */ - hb_hashmap_t& operator << (const hb_pair_t& v) - { set (v.first, v.second); return *this; } - - protected: - - template - bool set_with_hash (K key, uint32_t hash, VV&& value, bool is_delete=false) + /* C iterator. */ + bool next (int *idx, + K *key, + V *value) const { - if (unlikely (!successful)) return false; - if (unlikely ((occupancy + occupancy / 2) >= mask && !resize ())) return false; - unsigned int i = bucket_for_hash (key, hash); + unsigned i = (unsigned) (*idx + 1); - if (is_delete && items[i].key != key) - return true; /* Trying to delete non-existent key. */ + unsigned count = size (); + while (i < count && !items[i].is_real ()) + i++; - if (items[i].is_used ()) + if (i >= count) { - occupancy--; - if (!items[i].is_tombstone ()) - population--; + *idx = -1; + return false; } - items[i].key = key; - items[i].value = std::forward (value); - items[i].hash = hash; - items[i].set_used (true); - items[i].set_tombstone (is_delete); - - occupancy++; - if (!is_delete) - population++; + *key = items[i].key; + *value = items[i].value; + *idx = (signed) i; return true; } - unsigned int bucket_for (const K &key) const - { - return bucket_for_hash (key, hb_hash (key)); - } - - unsigned int bucket_for_hash (const K &key, uint32_t hash) const + /* Sink interface. */ + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (v.first, v.second); return *this; } + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (v.first, std::move (v.second)); return *this; } + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (std::move (v.first), v.second); return *this; } + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (std::move (v.first), std::move (v.second)); return *this; } + + item_t& item_for_hash (const K &key, uint32_t hash) const { hash &= 0x3FFFFFFF; // We only store lower 30bit of hash unsigned int i = hash % prime; @@ -359,12 +410,12 @@ struct hb_hashmap_t while (items[i].is_used ()) { if (items[i].hash == hash && items[i] == key) - return i; + return items[i]; if (tombstone == (unsigned) -1 && items[i].is_tombstone ()) tombstone = i; i = (i + ++step) & mask; } - return tombstone == (unsigned) -1 ? i : tombstone; + return items[tombstone == (unsigned) -1 ? i : tombstone]; } static unsigned int prime_for (unsigned int shift) @@ -443,4 +494,5 @@ struct hb_map_t : hb_hashmap_t auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_ident template using hb_add_pointer = decltype (_hb_try_add_pointer (hb_prioritize)); -/* TODO Add feature-parity to std::decay. */ -template using hb_decay = hb_remove_const>; +template using hb_decay = typename std::decay::type; #define hb_is_convertible(From,To) std::is_convertible::value @@ -133,6 +132,18 @@ struct template constexpr auto operator () (T *v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (const hb::shared_ptr& v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (hb::shared_ptr& v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (const hb::unique_ptr& v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (hb::unique_ptr& v) const HB_AUTO_RETURN (*v) } HB_FUNCOBJ (hb_deref); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-multimap.hh b/src/java.desktop/share/native/libharfbuzz/hb-multimap.hh new file mode 100644 index 00000000000..b4a8cc62a3e --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-multimap.hh @@ -0,0 +1,92 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_MULTIMAP_HH +#define HB_MULTIMAP_HH + +#include "hb.hh" +#include "hb-map.hh" +#include "hb-vector.hh" + + +/* + * hb_multimap_t + */ + +struct hb_multimap_t +{ + void add (hb_codepoint_t k, hb_codepoint_t v) + { + hb_codepoint_t *i; + if (multiples_indices.has (k, &i)) + { + multiples_values[*i].push (v); + return; + } + + hb_codepoint_t *old_v; + if (singulars.has (k, &old_v)) + { + hb_codepoint_t old = *old_v; + singulars.del (k); + + multiples_indices.set (k, multiples_values.length); + auto *vec = multiples_values.push (); + + vec->push (old); + vec->push (v); + + return; + } + + singulars.set (k, v); + } + + hb_array_t get (hb_codepoint_t k) const + { + const hb_codepoint_t *v; + if (singulars.has (k, &v)) + return hb_array (v, 1); + + hb_codepoint_t *i; + if (multiples_indices.has (k, &i)) + return multiples_values[*i].as_array (); + + return hb_array_t (); + } + + bool in_error () const + { + return singulars.in_error () || multiples_indices.in_error () || multiples_values.in_error (); + } + + protected: + hb_map_t singulars; + hb_map_t multiples_indices; + hb_vector_t> multiples_values; +}; + + + +#endif /* HB_MULTIMAP_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh b/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh index 8b62afaf94e..72012bd2e64 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh @@ -60,7 +60,7 @@ typedef pthread_mutex_t hb_mutex_impl_t; #elif !defined(HB_NO_MT) && !defined(HB_MUTEX_IMPL_STD_MUTEX) && defined(_WIN32) typedef CRITICAL_SECTION hb_mutex_impl_t; -#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) #define hb_mutex_impl_init(M) InitializeCriticalSectionEx (M, 0, 0) #else #define hb_mutex_impl_init(M) InitializeCriticalSection (M) @@ -97,6 +97,9 @@ struct hb_mutex_t /* Create space for, but do not initialize m. */ alignas(hb_mutex_impl_t) char m[sizeof (hb_mutex_impl_t)]; + hb_mutex_t () { init (); } + ~hb_mutex_t () { fini (); } + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" void init () { hb_mutex_impl_init ((hb_mutex_impl_t *) m); } @@ -108,10 +111,11 @@ struct hb_mutex_t struct hb_lock_t { - hb_lock_t (hb_mutex_t &mutex_) : mutex (mutex_) { mutex.lock (); } - ~hb_lock_t () { mutex.unlock (); } + hb_lock_t (hb_mutex_t &mutex_) : mutex (&mutex_) { mutex->lock (); } + hb_lock_t (hb_mutex_t *mutex_) : mutex (mutex_) { if (mutex) mutex->lock (); } + ~hb_lock_t () { if (mutex) mutex->unlock (); } private: - hb_mutex_t &mutex; + hb_mutex_t *mutex; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-null.hh b/src/java.desktop/share/native/libharfbuzz/hb-null.hh index 4403867f5c3..8a9ebbfc2d7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-null.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-null.hh @@ -39,6 +39,24 @@ #define HB_NULL_POOL_SIZE 448 +template +struct _hb_has_min_size : hb_false_type {}; +template +struct _hb_has_min_size> + : hb_true_type {}; +template +using hb_has_min_size = _hb_has_min_size; +#define hb_has_min_size(T) hb_has_min_size::value + +template +struct _hb_has_null_size : hb_false_type {}; +template +struct _hb_has_null_size> + : hb_true_type {}; +template +using hb_has_null_size = _hb_has_null_size; +#define hb_has_null_size(T) hb_has_null_size::value + /* Use SFINAE to sniff whether T has min_size; in which case return the larger * of sizeof(T) and T::null_size, otherwise return sizeof(T). * @@ -117,8 +135,19 @@ struct NullHelper }; \ namespace Namespace { \ static_assert (true, "") /* Require semicolon after. */ +#define DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1(Namespace, Type, Size) \ + } /* Close namespace. */ \ + extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Size]; \ + template \ + struct Null> { \ + static Namespace::Type const & get_null () { \ + return *reinterpret_cast *> (_hb_Null_##Namespace##_##Type); \ + } \ + }; \ + namespace Namespace { \ + static_assert (true, "") /* Require semicolon after. */ #define DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \ - const unsigned char _hb_Null_##Namespace##_##Type[hb_null_size (Namespace::Type)] + const unsigned char _hb_Null_##Namespace##_##Type[sizeof (_hb_Null_##Namespace##_##Type)] /* Specializations for arbitrary-content Null objects expressed as struct initializer. */ #define DECLARE_NULL_INSTANCE(Type) \ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh b/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh index 9d2867e4835..07f3ebe0c7d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh @@ -31,7 +31,7 @@ #include "hb.hh" -#line 35 "hb-number-parser.hh" +#line 32 "hb-number-parser.hh" static const unsigned char _double_parser_trans_keys[] = { 0u, 0u, 43u, 57u, 46u, 57u, 48u, 57u, 43u, 57u, 48u, 57u, 48u, 101u, 48u, 57u, 46u, 101u, 0 @@ -135,12 +135,12 @@ strtod_rl (const char *p, const char **end_ptr /* IN/OUT */) int cs; -#line 139 "hb-number-parser.hh" +#line 132 "hb-number-parser.hh" { cs = double_parser_start; } -#line 144 "hb-number-parser.hh" +#line 135 "hb-number-parser.hh" { int _slen; int _trans; @@ -198,7 +198,7 @@ _resume: exp_overflow = true; } break; -#line 202 "hb-number-parser.hh" +#line 187 "hb-number-parser.hh" } _again: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-number.cc b/src/java.desktop/share/native/libharfbuzz/hb-number.cc index f4ce693d8ed..37bc8b87399 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-number.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-number.cc @@ -24,7 +24,6 @@ */ #include "hb.hh" -#include "hb-machinery.hh" #include "hb-number.hh" #include "hb-number-parser.hh" diff --git a/src/java.desktop/share/native/libharfbuzz/hb-object.hh b/src/java.desktop/share/native/libharfbuzz/hb-object.hh index 436c37aa1b7..ef675f835f1 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-object.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-object.hh @@ -69,7 +69,7 @@ struct hb_lockable_set_t item = items.push (v); l.unlock (); } - return item; + return items.in_error () ? nullptr : item; } template @@ -80,7 +80,7 @@ struct hb_lockable_set_t if (item) { item_t old = *item; - *item = items[items.length - 1]; + *item = std::move (items.tail ()); items.pop (); l.unlock (); old.fini (); @@ -123,7 +123,7 @@ struct hb_lockable_set_t l.lock (); while (items.length) { - item_t old = items[items.length - 1]; + item_t old = items.tail (); items.pop (); l.unlock (); old.fini (); @@ -144,14 +144,14 @@ struct hb_reference_count_t { mutable hb_atomic_int_t ref_count; - void init (int v = 1) { ref_count.set_relaxed (v); } - int get_relaxed () const { return ref_count.get_relaxed (); } + void init (int v = 1) { ref_count = v; } + int get_relaxed () const { return ref_count; } int inc () const { return ref_count.inc (); } int dec () const { return ref_count.dec (); } - void fini () { ref_count.set_relaxed (-0x0000DEAD); } + void fini () { ref_count = -0x0000DEAD; } - bool is_inert () const { return !ref_count.get_relaxed (); } - bool is_valid () const { return ref_count.get_relaxed () > 0; } + bool is_inert () const { return !ref_count; } + bool is_valid () const { return ref_count > 0; } }; @@ -175,14 +175,34 @@ struct hb_user_data_array_t void init () { lock.init (); items.init (); } - HB_INTERNAL bool set (hb_user_data_key_t *key, - void * data, - hb_destroy_func_t destroy, - hb_bool_t replace); + void fini () { items.fini (lock); lock.fini (); } + + bool set (hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) + { + if (!key) + return false; - HB_INTERNAL void *get (hb_user_data_key_t *key); + if (replace) { + if (!data && !destroy) { + items.remove (key, lock); + return true; + } + } + hb_user_data_item_t item = {key, data, destroy}; + bool ret = !!items.replace_or_insert (item, lock, (bool) replace); - void fini () { items.fini (lock); lock.fini (); } + return ret; + } + + void *get (hb_user_data_key_t *key) + { + hb_user_data_item_t item = {nullptr, nullptr, nullptr}; + + return items.find (key, &item, lock) ? item.data : nullptr; + } }; @@ -214,23 +234,26 @@ static inline void hb_object_trace (const Type *obj, const char *function) obj ? obj->header.ref_count.get_relaxed () : 0); } -template -static inline Type *hb_object_create () +template +static inline Type *hb_object_create (Ts... ds) { Type *obj = (Type *) hb_calloc (1, sizeof (Type)); if (unlikely (!obj)) return obj; + new (obj) Type (std::forward (ds)...); + hb_object_init (obj); hb_object_trace (obj, HB_FUNC); + return obj; } template static inline void hb_object_init (Type *obj) { obj->header.ref_count.init (); - obj->header.writable.set_relaxed (true); + obj->header.writable = true; obj->header.user_data.init (); } template @@ -241,12 +264,12 @@ static inline bool hb_object_is_valid (const Type *obj) template static inline bool hb_object_is_immutable (const Type *obj) { - return !obj->header.writable.get_relaxed (); + return !obj->header.writable; } template static inline void hb_object_make_immutable (const Type *obj) { - obj->header.writable.set_relaxed (false); + obj->header.writable = false; } template static inline Type *hb_object_reference (Type *obj) @@ -269,18 +292,22 @@ static inline bool hb_object_destroy (Type *obj) return false; hb_object_fini (obj); + + if (!std::is_trivially_destructible::value) + obj->~Type (); + return true; } template static inline void hb_object_fini (Type *obj) { obj->header.ref_count.fini (); /* Do this before user_data */ - hb_user_data_array_t *user_data = obj->header.user_data.get (); + hb_user_data_array_t *user_data = obj->header.user_data.get_acquire (); if (user_data) { user_data->fini (); hb_free (user_data); - user_data = nullptr; + obj->header.user_data.set_relaxed (nullptr); } } template @@ -295,7 +322,7 @@ static inline bool hb_object_set_user_data (Type *obj, assert (hb_object_is_valid (obj)); retry: - hb_user_data_array_t *user_data = obj->header.user_data.get (); + hb_user_data_array_t *user_data = obj->header.user_data.get_acquire (); if (unlikely (!user_data)) { user_data = (hb_user_data_array_t *) hb_calloc (sizeof (hb_user_data_array_t), 1); @@ -320,7 +347,7 @@ static inline void *hb_object_get_user_data (Type *obj, if (unlikely (!obj || obj->header.is_inert ())) return nullptr; assert (hb_object_is_valid (obj)); - hb_user_data_array_t *user_data = obj->header.user_data.get (); + hb_user_data_array_t *user_data = obj->header.user_data.get_acquire (); if (!user_data) return nullptr; return user_data->get (key); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh b/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh index 1dff7c414cc..c02eb41d100 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh @@ -90,7 +90,7 @@ typedef struct OpenTypeOffsetTable { if (table_count) { - + tables.sub_array (start_offset, table_count) + + tables.as_array ().sub_array (start_offset, table_count) | hb_map (&TableRecord::tag) | hb_sink (hb_array (table_tags, *table_count)) ; @@ -158,7 +158,7 @@ typedef struct OpenTypeOffsetTable return_trace (false); if (likely (len)) - memcpy (start, blob->data, len); + hb_memcpy (start, blob->data, len); /* 4-byte alignment. */ c->align (4); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh b/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh index a4dfb3615e3..4639d80babc 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh @@ -105,7 +105,7 @@ struct IntType bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); + return_trace (c->check_struct (this)); } protected: BEInt v; @@ -141,27 +141,29 @@ typedef HBINT32 FWORD32; /* 16-bit unsigned integer (HBUINT16) that describes a quantity in FUnits. */ typedef HBUINT16 UFWORD; -/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */ -struct F2DOT14 : HBINT16 +template +struct HBFixed : Type { - F2DOT14& operator = (uint16_t i ) { HBINT16::operator= (i); return *this; } - // 16384 means 1<<14 - float to_float () const { return ((int32_t) v) / 16384.f; } - void set_float (float f) { v = roundf (f * 16384.f); } + static constexpr float shift = (float) (1 << fraction_bits); + static_assert (Type::static_size * 8 > fraction_bits, ""); + + operator signed () const = delete; + operator unsigned () const = delete; + typename Type::type to_int () const { return Type::v; } + void set_int (typename Type::type i ) { Type::v = i; } + float to_float (float offset = 0) const { return ((int32_t) Type::v + offset) / shift; } + void set_float (float f) { Type::v = roundf (f * shift); } public: - DEFINE_SIZE_STATIC (2); + DEFINE_SIZE_STATIC (Type::static_size); }; +/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */ +using F2DOT14 = HBFixed; +using F4DOT12 = HBFixed; +using F6DOT10 = HBFixed; + /* 32-bit signed fixed-point number (16.16). */ -struct HBFixed : HBINT32 -{ - HBFixed& operator = (uint32_t i) { HBINT32::operator= (i); return *this; } - // 65536 means 1<<16 - float to_float () const { return ((int32_t) v) / 65536.f; } - void set_float (float f) { v = roundf (f * 65536.f); } - public: - DEFINE_SIZE_STATIC (4); -}; +using F16DOT16 = HBFixed; /* Date represented in number of seconds since 12:00 midnight, January 1, * 1904. The value is represented as a signed 64-bit integer. */ @@ -170,7 +172,7 @@ struct LONGDATETIME bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); + return_trace (c->check_struct (this)); } protected: HBINT32 major; @@ -196,6 +198,10 @@ struct HBGlyphID16 : HBUINT16 { HBGlyphID16& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } }; +struct HBGlyphID24 : HBUINT24 +{ + HBGlyphID24& operator = (uint32_t i) { HBUINT24::operator= (i); return *this; } +}; /* Script/language-system/feature index */ struct Index : HBUINT16 { @@ -208,6 +214,12 @@ typedef Index NameID; struct VarIdx : HBUINT32 { static constexpr unsigned NO_VARIATION = 0xFFFFFFFFu; + static_assert (NO_VARIATION == HB_OT_LAYOUT_NO_VARIATIONS_INDEX, ""); + static uint32_t add (uint32_t i, unsigned short v) + { + if (i == NO_VARIATION) return i; + return i + v; + } VarIdx& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; } }; DECLARE_NULL_NAMESPACE_BYTES (OT, VarIdx); @@ -300,6 +312,10 @@ struct _hb_has_null template struct OffsetTo : Offset { + // Make sure Type is not unbounded; works only for types that are fully defined at OffsetTo time. + static_assert (has_null == false || + (hb_has_null_size (Type) || !hb_has_min_size (Type)), ""); + HB_DELETE_COPY_ASSIGN (OffsetTo); OffsetTo () = default; @@ -450,14 +466,16 @@ struct UnsizedArrayOf { unsigned int i = (unsigned int) i_; const Type *p = &arrayZ[i]; - if (unlikely (p < arrayZ)) return Null (Type); /* Overflowed. */ + if (unlikely ((const void *) p < (const void *) arrayZ)) return Null (Type); /* Overflowed. */ + _hb_compiler_memory_r_barrier (); return *p; } Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; Type *p = &arrayZ[i]; - if (unlikely (p < arrayZ)) return Crap (Type); /* Overflowed. */ + if (unlikely ((const void *) p < (const void *) arrayZ)) return Crap (Type); /* Overflowed. */ + _hb_compiler_memory_r_barrier (); return *p; } @@ -486,10 +504,10 @@ struct UnsizedArrayOf void qsort (unsigned int len, unsigned int start = 0, unsigned int end = (unsigned int) -1) { as_array (len).qsort (start, end); } - bool serialize (hb_serialize_context_t *c, unsigned int items_len) + bool serialize (hb_serialize_context_t *c, unsigned int items_len, bool clear = true) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend (this, items_len))) return_trace (false); + if (unlikely (!c->extend_size (this, get_size (items_len), clear))) return_trace (false); return_trace (true); } template *p = &this->arrayZ[i]; - if (unlikely (p < this->arrayZ)) return Null (Type); /* Overflowed. */ + if (unlikely ((const void *) p < (const void *) this->arrayZ)) return Null (Type); /* Overflowed. */ + _hb_compiler_memory_r_barrier (); return this+*p; } Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; const OffsetTo *p = &this->arrayZ[i]; - if (unlikely (p < this->arrayZ)) return Crap (Type); /* Overflowed. */ + if (unlikely ((const void *) p < (const void *) this->arrayZ)) return Crap (Type); /* Overflowed. */ + _hb_compiler_memory_r_barrier (); return this+*p; } @@ -608,12 +628,14 @@ struct ArrayOf { unsigned int i = (unsigned int) i_; if (unlikely (i >= len)) return Null (Type); + _hb_compiler_memory_r_barrier (); return arrayZ[i]; } Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; if (unlikely (i >= len)) return Crap (Type); + _hb_compiler_memory_r_barrier (); return arrayZ[i]; } @@ -635,14 +657,9 @@ struct ArrayOf operator iter_t () const { return iter (); } operator writer_t () { return writer (); } - hb_array_t sub_array (unsigned int start_offset, unsigned int count) const - { return as_array ().sub_array (start_offset, count); } - hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const - { return as_array ().sub_array (start_offset, count); } - hb_array_t sub_array (unsigned int start_offset, unsigned int count) - { return as_array ().sub_array (start_offset, count); } - hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) - { return as_array ().sub_array (start_offset, count); } + /* Faster range-based for loop. */ + const Type *begin () const { return arrayZ; } + const Type *end () const { return arrayZ + len; } template Type &lsearch (const T &x, Type ¬_found = Crap (Type)) @@ -656,15 +673,15 @@ struct ArrayOf unsigned int to_store = (unsigned int) -1) const { return as_array ().lfind (x, i, not_found, to_store); } - void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1) - { as_array ().qsort (start, end); } + void qsort () + { as_array ().qsort (); } - HB_NODISCARD bool serialize (hb_serialize_context_t *c, unsigned items_len) + HB_NODISCARD bool serialize (hb_serialize_context_t *c, unsigned items_len, bool clear = true) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (this))) return_trace (false); c->check_assign (len, items_len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW); - if (unlikely (!c->extend (this))) return_trace (false); + if (unlikely (!c->extend_size (this, get_size (), clear))) return_trace (false); return_trace (true); } template using Array16Of = ArrayOf; +template using Array24Of = ArrayOf; template using Array32Of = ArrayOf; using PString = ArrayOf; @@ -738,26 +756,28 @@ template using Array16OfOffset32To = ArrayOf using Array32OfOffset32To = ArrayOf, HBUINT32>; /* Array of offsets relative to the beginning of the array itself. */ -template -struct List16OfOffset16To : Array16OfOffset16To +template +struct List16OfOffsetTo : ArrayOf, HBUINT16> { const Type& operator [] (int i_) const { unsigned int i = (unsigned int) i_; if (unlikely (i >= this->len)) return Null (Type); + _hb_compiler_memory_r_barrier (); return this+this->arrayZ[i]; } const Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; if (unlikely (i >= this->len)) return Crap (Type); + _hb_compiler_memory_r_barrier (); return this+this->arrayZ[i]; } bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - struct List16OfOffset16To *out = c->serializer->embed (*this); + struct List16OfOffsetTo *out = c->serializer->embed (*this); if (unlikely (!out)) return_trace (false); unsigned int count = this->len; for (unsigned int i = 0; i < count; i++) @@ -769,10 +789,13 @@ struct List16OfOffset16To : Array16OfOffset16To bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); - return_trace (Array16OfOffset16To::sanitize (c, this, std::forward (ds)...)); + return_trace ((Array16Of>::sanitize (c, this, std::forward (ds)...))); } }; +template +using List16OfOffset16To = List16OfOffsetTo; + /* An array starting at second element. */ template struct HeadlessArrayOf @@ -785,12 +808,14 @@ struct HeadlessArrayOf { unsigned int i = (unsigned int) i_; if (unlikely (i >= lenP1 || !i)) return Null (Type); + _hb_compiler_memory_r_barrier (); return arrayZ[i-1]; } Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; if (unlikely (i >= lenP1 || !i)) return Crap (Type); + _hb_compiler_memory_r_barrier (); return arrayZ[i-1]; } unsigned int get_size () const @@ -809,21 +834,25 @@ struct HeadlessArrayOf operator iter_t () const { return iter (); } operator writer_t () { return writer (); } - bool serialize (hb_serialize_context_t *c, unsigned int items_len) + /* Faster range-based for loop. */ + const Type *begin () const { return arrayZ; } + const Type *end () const { return arrayZ + get_length (); } + + HB_NODISCARD bool serialize (hb_serialize_context_t *c, unsigned int items_len, bool clear = true) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (this))) return_trace (false); c->check_assign (lenP1, items_len + 1, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW); - if (unlikely (!c->extend (this))) return_trace (false); + if (unlikely (!c->extend_size (this, get_size (), clear))) return_trace (false); return_trace (true); } template - bool serialize (hb_serialize_context_t *c, Iterator items) + HB_NODISCARD bool serialize (hb_serialize_context_t *c, Iterator items) { TRACE_SERIALIZE (this); - unsigned count = items.len (); - if (unlikely (!serialize (c, count))) return_trace (false); + unsigned count = hb_len (items); + if (unlikely (!serialize (c, count, false))) return_trace (false); /* TODO Umm. Just exhaust the iterator instead? Being extra * cautious right now.. */ for (unsigned i = 0; i < count; i++, ++items) @@ -869,12 +898,14 @@ struct ArrayOfM1 { unsigned int i = (unsigned int) i_; if (unlikely (i > lenM1)) return Null (Type); + _hb_compiler_memory_r_barrier (); return arrayZ[i]; } Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; if (unlikely (i > lenM1)) return Crap (Type); + _hb_compiler_memory_r_barrier (); return arrayZ[i]; } unsigned int get_size () const @@ -923,14 +954,9 @@ struct SortedArrayOf : ArrayOf operator iter_t () const { return iter (); } operator writer_t () { return writer (); } - hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int count) const - { return as_array ().sub_array (start_offset, count); } - hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const - { return as_array ().sub_array (start_offset, count); } - hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int count) - { return as_array ().sub_array (start_offset, count); } - hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) - { return as_array ().sub_array (start_offset, count); } + /* Faster range-based for loop. */ + const Type *begin () const { return this->arrayZ; } + const Type *end () const { return this->arrayZ + this->len; } bool serialize (hb_serialize_context_t *c, unsigned int items_len) { @@ -961,6 +987,7 @@ struct SortedArrayOf : ArrayOf }; template using SortedArray16Of = SortedArrayOf; +template using SortedArray24Of = SortedArrayOf; template using SortedArray32Of = SortedArrayOf; /* @@ -1053,12 +1080,14 @@ struct VarSizedBinSearchArrayOf { unsigned int i = (unsigned int) i_; if (unlikely (i >= get_length ())) return Null (Type); + _hb_compiler_memory_r_barrier (); return StructAtOffset (&bytesZ, i * header.unitSize); } Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; if (unlikely (i >= get_length ())) return Crap (Type); + _hb_compiler_memory_r_barrier (); return StructAtOffset (&bytesZ, i * header.unitSize); } unsigned int get_length () const diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh index 54c7ecdf294..d31a328e8ad 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh @@ -66,95 +66,25 @@ struct CFFIndex { TRACE_SERIALIZE (this); unsigned int size = get_size (); - CFFIndex *out = c->allocate_size (size); + CFFIndex *out = c->allocate_size (size, false); if (likely (out)) - memcpy (out, this, size); + hb_memcpy (out, this, size); return_trace (out); } + template bool serialize (hb_serialize_context_t *c, - unsigned int offSize_, - const byte_str_array_t &byteArray) + const Iterable &iterable) { TRACE_SERIALIZE (this); - - if (byteArray.length == 0) - { - COUNT *dest = c->allocate_min (); - if (unlikely (!dest)) return_trace (false); - *dest = 0; - return_trace (true); - } - - /* serialize CFFIndex header */ - if (unlikely (!c->extend_min (this))) return_trace (false); - this->count = byteArray.length; - this->offSize = offSize_; - if (unlikely (!c->allocate_size (offSize_ * (byteArray.length + 1)))) - return_trace (false); - - /* serialize indices */ - unsigned int offset = 1; - unsigned int i = 0; - for (; i < byteArray.length; i++) - { - set_offset_at (i, offset); - offset += byteArray[i].get_size (); - } - set_offset_at (i, offset); - - /* serialize data */ - for (unsigned int i = 0; i < byteArray.length; i++) - { - const hb_ubytes_t &bs = byteArray[i]; - unsigned char *dest = c->allocate_size (bs.length); - if (unlikely (!dest)) return_trace (false); - memcpy (dest, &bs[0], bs.length); - } - - return_trace (true); - } - - bool serialize (hb_serialize_context_t *c, - unsigned int offSize_, - const str_buff_vec_t &buffArray) - { - byte_str_array_t byteArray; - byteArray.init (); - byteArray.resize (buffArray.length); - for (unsigned int i = 0; i < byteArray.length; i++) - byteArray[i] = hb_ubytes_t (buffArray[i].arrayZ, buffArray[i].length); - bool result = this->serialize (c, offSize_, byteArray); - byteArray.fini (); - return result; - } - - template - bool serialize (hb_serialize_context_t *c, - Iterator it) - { - TRACE_SERIALIZE (this); - serialize_header(c, + it | hb_map ([] (const hb_ubytes_t &_) { return _.length; })); + auto it = hb_iter (iterable); + serialize_header(c, + it | hb_map (hb_iter) | hb_map (hb_len)); for (const auto &_ : +it) - _.copy (c); + hb_iter (_).copy (c); return_trace (true); } - bool serialize (hb_serialize_context_t *c, - const byte_str_array_t &byteArray) - { return serialize (c, + hb_iter (byteArray)); } - - bool serialize (hb_serialize_context_t *c, - const str_buff_vec_t &buffArray) - { - auto it = - + hb_iter (buffArray) - | hb_map ([] (const str_buff_t &_) { return hb_ubytes_t (_.arrayZ, _.length); }) - ; - return serialize (c, it); - } - template bool serialize_header (hb_serialize_context_t *c, @@ -171,7 +101,7 @@ struct CFFIndex if (!this->count) return_trace (true); if (unlikely (!c->extend (this->offSize))) return_trace (false); this->offSize = off_size; - if (unlikely (!c->allocate_size (off_size * (this->count + 1)))) + if (unlikely (!c->allocate_size (off_size * (this->count + 1), false))) return_trace (false); /* serialize indices */ @@ -179,14 +109,27 @@ struct CFFIndex unsigned int i = 0; for (unsigned _ : +it) { - CFFIndex::set_offset_at (i++, offset); + set_offset_at (i++, offset); offset += _; } - CFFIndex::set_offset_at (i, offset); + set_offset_at (i, offset); return_trace (true); } + template + static unsigned total_size (const Iterable &iterable) + { + auto it = + hb_iter (iterable) | hb_map (hb_iter) | hb_map (hb_len); + if (!it) return 0; + + unsigned total = + it | hb_reduce (hb_add, 0); + unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8; + + return min_size + HBUINT8::static_size + (hb_len (it) + 1) * off_size + total; + } + void set_offset_at (unsigned int index, unsigned int offset) { assert (index <= count); @@ -207,10 +150,14 @@ struct CFFIndex unsigned int size = offSize; const HBUINT8 *p = offsets + size * index; - unsigned int offset = 0; - for (; size; size--) - offset = (offset << 8) + *p++; - return offset; + switch (size) + { + case 1: return * (HBUINT8 *) p; + case 2: return * (HBUINT16 *) p; + case 3: return * (HBUINT24 *) p; + case 4: return * (HBUINT32 *) p; + default: return 0; + } } unsigned int length_at (unsigned int index) const @@ -229,6 +176,7 @@ struct CFFIndex hb_ubytes_t operator [] (unsigned int index) const { if (unlikely (index >= count)) return hb_ubytes_t (); + _hb_compiler_memory_r_barrier (); unsigned length = length_at (index); if (unlikely (!length)) return hb_ubytes_t (); return hb_ubytes_t (data_base () + offset_at (index) - 1, length); @@ -280,7 +228,7 @@ struct CFFIndexOf : CFFIndex if (unlikely (!c->extend_min (this))) return_trace (false); this->count = dataArrayLen; this->offSize = offSize_; - if (unlikely (!c->allocate_size (offSize_ * (dataArrayLen + 1)))) + if (unlikely (!c->allocate_size (offSize_ * (dataArrayLen + 1), false))) return_trace (false); /* serialize indices */ @@ -288,10 +236,10 @@ struct CFFIndexOf : CFFIndex unsigned int i = 0; for (; i < dataArrayLen; i++) { - CFFIndex::set_offset_at (i, offset); + this->set_offset_at (i, offset); offset += dataSizeArray[i]; } - CFFIndex::set_offset_at (i, offset); + this->set_offset_at (i, offset); /* serialize data */ for (unsigned int i = 0; i < dataArrayLen; i++) @@ -324,13 +272,12 @@ struct Dict : UnsizedByteStr template static bool serialize_int_op (hb_serialize_context_t *c, op_code_t op, V value, op_code_t intOp) { - // XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation - if (/*unlikely*/ (!serialize_int (c, intOp, value))) + if (unlikely ((!serialize_int (c, intOp, value)))) return false; TRACE_SERIALIZE (this); /* serialize the opcode */ - HBUINT8 *p = c->allocate_size (OpCode_Size (op)); + HBUINT8 *p = c->allocate_size (OpCode_Size (op), false); if (unlikely (!p)) return_trace (false); if (Is_OpCode_ESC (op)) { @@ -415,9 +362,8 @@ struct FDSelect0 { TRACE_SANITIZE (this); if (unlikely (!(c->check_struct (this)))) return_trace (false); - for (unsigned int i = 0; i < c->get_num_glyphs (); i++) - if (unlikely (!fds[i].sanitize (c))) - return_trace (false); + if (unlikely (!c->check_array (fds, c->get_num_glyphs ()))) + return_trace (false); return_trace (true); } @@ -471,14 +417,20 @@ struct FDSelect3_4 return_trace (true); } - hb_codepoint_t get_fd (hb_codepoint_t glyph) const + static int _cmp_range (const void *_key, const void *_item) { - unsigned int i; - for (i = 1; i < nRanges (); i++) - if (glyph < ranges[i].first) - break; + hb_codepoint_t glyph = * (hb_codepoint_t *) _key; + FDSelect3_4_Range *range = (FDSelect3_4_Range *) _item; - return (hb_codepoint_t) ranges[i - 1].fd; + if (glyph < range[0].first) return -1; + if (glyph < range[1].first) return 0; + return +1; + } + + hb_codepoint_t get_fd (hb_codepoint_t glyph) const + { + auto *range = hb_bsearch (glyph, &ranges[0], nRanges () - 1, sizeof (ranges[0]), _cmp_range); + return range ? range->fd : ranges[nRanges () - 1].fd; } GID_TYPE &nRanges () { return ranges.len; } @@ -501,9 +453,9 @@ struct FDSelect { TRACE_SERIALIZE (this); unsigned int size = src.get_size (num_glyphs); - FDSelect *dest = c->allocate_size (size); + FDSelect *dest = c->allocate_size (size, false); if (unlikely (!dest)) return_trace (false); - memcpy (dest, &src, size); + hb_memcpy (dest, &src, size); return_trace (true); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc index 9173b9d8600..505af15e5c5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc @@ -422,8 +422,8 @@ bool OT::cff1::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph } else { - extents->x_bearing = font->em_scalef_x (bounds.min.x.to_real ()); - extents->width = font->em_scalef_x (bounds.max.x.to_real ()) - extents->x_bearing; + extents->x_bearing = roundf (bounds.min.x.to_real ()); + extents->width = roundf (bounds.max.x.to_real () - extents->x_bearing); } if (bounds.min.y >= bounds.max.y) { @@ -432,10 +432,12 @@ bool OT::cff1::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph } else { - extents->y_bearing = font->em_scalef_y (bounds.max.y.to_real ()); - extents->height = font->em_scalef_y (bounds.min.y.to_real ()) - extents->y_bearing; + extents->y_bearing = roundf (bounds.max.y.to_real ()); + extents->height = roundf (bounds.min.y.to_real () - extents->y_bearing); } + font->scale_glyph_extents (extents); + return true; } @@ -551,6 +553,15 @@ bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoin return true; } +bool OT::cff1::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const +{ + funcs->push_clip_glyph (data, glyph, font); + funcs->color (data, true, foreground); + funcs->pop_clip (data); + + return true; +} + bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const { #ifdef HB_NO_OT_FONT_CFF diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh index b773956662f..60bc308f90c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh @@ -30,6 +30,7 @@ #include "hb-ot-cff-common.hh" #include "hb-subset-cff1.hh" #include "hb-draw.hh" +#include "hb-paint.hh" #define HB_STRING_ARRAY_NAME cff1_std_strings #define HB_STRING_ARRAY_LIST "hb-ot-cff1-std-str.hh" @@ -175,7 +176,7 @@ struct Encoding unsigned int size = src.get_size (); Encoding *dest = c->allocate_size (size); if (unlikely (!dest)) return_trace (false); - memcpy (dest, &src, size); + hb_memcpy (dest, &src, size); return_trace (true); } @@ -471,7 +472,7 @@ struct Charset unsigned int size = src.get_size (num_glyphs); Charset *dest = c->allocate_size (size); if (unlikely (!dest)) return_trace (false); - memcpy (dest, &src, size); + hb_memcpy (dest, &src, size); return_trace (true); } @@ -617,7 +618,6 @@ struct CFF1StringIndex : CFF1Index } byte_str_array_t bytesArray; - bytesArray.init (); if (!bytesArray.resize (sidmap.get_population ())) return_trace (false); for (unsigned int i = 0; i < strings.count; i++) @@ -628,7 +628,6 @@ struct CFF1StringIndex : CFF1Index } bool result = CFF1Index::serialize (c, bytesArray); - bytesArray.fini (); return_trace (result); } }; @@ -813,7 +812,7 @@ struct cff1_top_dict_opset_t : top_dict_opset_t break; default: - env.last_offset = env.str_ref.offset; + env.last_offset = env.str_ref.get_offset (); top_dict_opset_t::process_op (op, env, dictval); /* Record this operand below if stack is empty, otherwise done */ if (!env.argStack.is_empty ()) return; @@ -903,8 +902,6 @@ struct cff1_private_dict_opset_t : dict_opset_t case OpCode_FamilyOtherBlues: case OpCode_StemSnapH: case OpCode_StemSnapV: - env.clear_args (); - break; case OpCode_StdHW: case OpCode_StdVW: case OpCode_BlueScale: @@ -916,7 +913,6 @@ struct cff1_private_dict_opset_t : dict_opset_t case OpCode_initialRandomSeed: case OpCode_defaultWidthX: case OpCode_nominalWidthX: - val.single_val = env.argStack.pop_num (); env.clear_args (); break; case OpCode_Subrs: @@ -1295,10 +1291,10 @@ struct cff1 } protected: - hb_blob_t *blob = nullptr; hb_sanitize_context_t sc; public: + hb_blob_t *blob = nullptr; const Encoding *encoding = nullptr; const Charset *charset = nullptr; const CFF1NameIndex *nameIndex = nullptr; @@ -1345,6 +1341,7 @@ struct cff1 bool get_glyph_name (hb_codepoint_t glyph, char *buf, unsigned int buf_len) const { + if (unlikely (glyph >= num_glyphs)) return false; if (unlikely (!is_valid ())) return false; if (is_CID()) return false; if (unlikely (!buf_len)) return true; @@ -1379,7 +1376,7 @@ struct cff1 if (unlikely (!len)) return false; retry: - hb_sorted_vector_t *names = glyph_names.get (); + hb_sorted_vector_t *names = glyph_names.get_acquire (); if (unlikely (!names)) { names = (hb_sorted_vector_t *) hb_calloc (sizeof (hb_sorted_vector_t), 1); @@ -1428,6 +1425,7 @@ struct cff1 } HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; + HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const; HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const; HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc index 1e757ebfba9..ed430ee59d5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc @@ -124,8 +124,8 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, } else { - extents->x_bearing = font->em_scalef_x (param.min_x.to_real ()); - extents->width = font->em_scalef_x (param.max_x.to_real ()) - extents->x_bearing; + extents->x_bearing = roundf (param.min_x.to_real ()); + extents->width = roundf (param.max_x.to_real () - extents->x_bearing); } if (param.min_y >= param.max_y) { @@ -134,10 +134,21 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, } else { - extents->y_bearing = font->em_scalef_y (param.max_y.to_real ()); - extents->height = font->em_scalef_y (param.min_y.to_real ()) - extents->y_bearing; + extents->y_bearing = roundf (param.max_y.to_real ()); + extents->height = roundf (param.min_y.to_real () - extents->y_bearing); } + font->scale_glyph_extents (extents); + + return true; +} + +bool OT::cff2::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const +{ + funcs->push_clip_glyph (data, glyph, font); + funcs->color (data, true, foreground); + funcs->pop_clip (data); + return true; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh index 7e1a3d0dba1..bfbc26b96ec 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh @@ -30,6 +30,7 @@ #include "hb-ot-cff-common.hh" #include "hb-subset-cff2.hh" #include "hb-draw.hh" +#include "hb-paint.hh" namespace CFF { @@ -56,7 +57,7 @@ struct CFF2FDSelect unsigned int size = src.get_size (num_glyphs); CFF2FDSelect *dest = c->allocate_size (size); if (unlikely (!dest)) return_trace (false); - memcpy (dest, &src, size); + hb_memcpy (dest, &src, size); return_trace (true); } @@ -124,7 +125,7 @@ struct CFF2VariationStore unsigned int size_ = varStore->get_size (); CFF2VariationStore *dest = c->allocate_size (size_); if (unlikely (!dest)) return_trace (false); - memcpy (dest, varStore, size_); + hb_memcpy (dest, varStore, size_); return_trace (true); } @@ -282,9 +283,6 @@ struct cff2_private_dict_opset_t : dict_opset_t case OpCode_BlueFuzz: case OpCode_ExpansionFactor: case OpCode_LanguageGroup: - val.single_val = env.argStack.pop_num (); - env.clear_args (); - break; case OpCode_BlueValues: case OpCode_OtherBlues: case OpCode_FamilyBlues: @@ -483,13 +481,18 @@ struct cff2 blob = nullptr; } + hb_map_t *create_glyph_to_sid_map () const + { + return nullptr; + } + bool is_valid () const { return blob; } protected: - hb_blob_t *blob = nullptr; hb_sanitize_context_t sc; public: + hb_blob_t *blob = nullptr; cff2_top_dict_values_t topDict; const CFF2Subrs *globalSubrs = nullptr; const CFF2VariationStore *varStore = nullptr; @@ -511,6 +514,7 @@ struct cff2 HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; + HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const; HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh index 4b6e542fb87..4a7a693e201 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh @@ -31,6 +31,7 @@ #include "hb-ot-shaper-arabic-pua.hh" #include "hb-open-type.hh" #include "hb-set.hh" +#include "hb-cache.hh" /* * cmap -- Character to Glyph Index Mapping @@ -909,7 +910,7 @@ struct DefaultUVS : SortedArray32Of hb_codepoint_t first = arrayZ[i].startUnicodeValue; hb_codepoint_t last = hb_min ((hb_codepoint_t) (first + arrayZ[i].additionalCount), (hb_codepoint_t) HB_UNICODE_MAX); - out->add_range (first, hb_min (last, 0x10FFFFu)); + out->add_range (first, last); } } @@ -925,37 +926,75 @@ struct DefaultUVS : SortedArray32Of if (unlikely (!c->copy (len))) return nullptr; unsigned init_len = c->length (); - hb_codepoint_t lastCode = HB_MAP_VALUE_INVALID; - int count = -1; - - for (const UnicodeValueRange& _ : as_array ()) + if (this->len > unicodes->get_population () * hb_bit_storage ((unsigned) this->len)) { - for (const unsigned addcnt : hb_range ((unsigned) _.additionalCount + 1)) + hb_codepoint_t start = HB_SET_VALUE_INVALID; + hb_codepoint_t end = HB_SET_VALUE_INVALID; + + for (hb_codepoint_t u = HB_SET_VALUE_INVALID; + unicodes->next (&u);) { - unsigned curEntry = (unsigned) _.startUnicodeValue + addcnt; - if (!unicodes->has (curEntry)) continue; - count += 1; - if (lastCode == HB_MAP_VALUE_INVALID) - lastCode = curEntry; - else if (lastCode + count != curEntry) + if (!as_array ().bsearch (u)) + continue; + if (start == HB_SET_VALUE_INVALID) + { + start = u; + end = start - 1; + } + if (end + 1 != u || end - start == 255) { UnicodeValueRange rec; - rec.startUnicodeValue = lastCode; - rec.additionalCount = count - 1; + rec.startUnicodeValue = start; + rec.additionalCount = end - start; c->copy (rec); - - lastCode = curEntry; - count = 0; + start = u; } + end = u; + } + if (start != HB_SET_VALUE_INVALID) + { + UnicodeValueRange rec; + rec.startUnicodeValue = start; + rec.additionalCount = end - start; + c->copy (rec); } - } - if (lastCode != HB_MAP_VALUE_INVALID) + } + else { - UnicodeValueRange rec; - rec.startUnicodeValue = lastCode; - rec.additionalCount = count; - c->copy (rec); + hb_codepoint_t lastCode = HB_SET_VALUE_INVALID; + int count = -1; + + for (const UnicodeValueRange& _ : *this) + { + hb_codepoint_t curEntry = (hb_codepoint_t) (_.startUnicodeValue - 1); + hb_codepoint_t end = curEntry + _.additionalCount + 2; + + for (; unicodes->next (&curEntry) && curEntry < end;) + { + count += 1; + if (lastCode == HB_SET_VALUE_INVALID) + lastCode = curEntry; + else if (lastCode + count != curEntry) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count - 1; + c->copy (rec); + + lastCode = curEntry; + count = 0; + } + } + } + + if (lastCode != HB_MAP_VALUE_INVALID) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count; + c->copy (rec); + } } if (c->length () - init_len == 0) @@ -1376,7 +1415,7 @@ struct CmapSubtable switch (format) { case 4: return u.format4.serialize (c, it); case 12: return u.format12.serialize (c, it); - case 14: return u.format14.serialize (c, plan->unicodes, plan->glyphs_requested, plan->glyph_map, base); + case 14: return u.format14.serialize (c, &plan->unicodes, &plan->glyphs_requested, plan->glyph_map, base); default: return; } } @@ -1474,19 +1513,67 @@ struct EncodingRecord DEFINE_SIZE_STATIC (8); }; +struct cmap; + struct SubtableUnicodesCache { private: - const void* base; - hb_hashmap_t> cached_unicodes; + hb_blob_ptr_t base_blob; + const char* base; + hb_hashmap_t> cached_unicodes; public: + + static SubtableUnicodesCache* create (hb_blob_ptr_t source_table) + { + SubtableUnicodesCache* cache = + (SubtableUnicodesCache*) hb_malloc (sizeof(SubtableUnicodesCache)); + new (cache) SubtableUnicodesCache (source_table); + return cache; + } + + static void destroy (void* value) { + if (!value) return; + + SubtableUnicodesCache* cache = (SubtableUnicodesCache*) value; + cache->~SubtableUnicodesCache (); + hb_free (cache); + } + SubtableUnicodesCache(const void* cmap_base) - : base(cmap_base), cached_unicodes() {} + : base_blob(), + base ((const char*) cmap_base), + cached_unicodes () + {} + + SubtableUnicodesCache(hb_blob_ptr_t base_blob_) + : base_blob(base_blob_), + base ((const char *) base_blob.get()), + cached_unicodes () + {} + + ~SubtableUnicodesCache() + { + base_blob.destroy (); + } - hb_set_t* set_for (const EncodingRecord* record) + bool same_base(const void* other) const { - if (!cached_unicodes.has ((intptr_t) record)) + return other == (const void*) base; + } + + const hb_set_t* set_for (const EncodingRecord* record, + SubtableUnicodesCache& mutable_cache) const + { + if (cached_unicodes.has ((unsigned) ((const char *) record - base))) + return cached_unicodes.get ((unsigned) ((const char *) record - base)); + + return mutable_cache.set_for (record); + } + + const hb_set_t* set_for (const EncodingRecord* record) + { + if (!cached_unicodes.has ((unsigned) ((const char *) record - base))) { hb_set_t *s = hb_set_create (); if (unlikely (s->in_error ())) @@ -1494,12 +1581,12 @@ struct SubtableUnicodesCache { (base+record->subtable).collect_unicodes (s); - if (unlikely (!cached_unicodes.set ((intptr_t) record, hb::unique_ptr {s}))) + if (unlikely (!cached_unicodes.set ((unsigned) ((const char *) record - base), hb::unique_ptr {s}))) return hb_set_get_empty (); return s; } - return cached_unicodes.get ((intptr_t) record); + return cached_unicodes.get ((unsigned) ((const char *) record - base)); } }; @@ -1523,13 +1610,30 @@ struct cmap { static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap; + + static SubtableUnicodesCache* create_filled_cache(hb_blob_ptr_t source_table) { + const cmap* cmap = source_table.get(); + auto it = + + hb_iter (cmap->encodingRecord) + | hb_filter ([&](const EncodingRecord& _) { + return cmap::filter_encoding_records_for_subset (cmap, _); + }) + ; + + SubtableUnicodesCache* cache = SubtableUnicodesCache::create(source_table); + for (const EncodingRecord& _ : it) + cache->set_for(&_); // populate the cache for this encoding record. + + return cache; + } + template bool serialize (hb_serialize_context_t *c, Iterator it, EncodingRecIter encodingrec_iter, const void *base, - const hb_subset_plan_t *plan, + hb_subset_plan_t *plan, bool drop_format_4 = false) { if (unlikely (!c->extend_min ((*this)))) return false; @@ -1538,7 +1642,14 @@ struct cmap unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0; auto snap = c->snapshot (); - SubtableUnicodesCache unicodes_cache (base); + SubtableUnicodesCache local_unicodes_cache (base); + const SubtableUnicodesCache* unicodes_cache = &local_unicodes_cache; + + if (plan->accelerator && + plan->accelerator->cmap_cache && + plan->accelerator->cmap_cache->same_base (base)) + unicodes_cache = plan->accelerator->cmap_cache; + for (const EncodingRecord& _ : encodingrec_iter) { if (c->in_error ()) @@ -1547,7 +1658,7 @@ struct cmap unsigned format = (base+_.subtable).u.format; if (format != 4 && format != 12 && format != 14) continue; - hb_set_t* unicodes_set = unicodes_cache.set_for (&_); + const hb_set_t* unicodes_set = unicodes_cache->set_for (&_, local_unicodes_cache); if (!drop_format_4 && format == 4) { @@ -1566,7 +1677,13 @@ struct cmap else if (format == 12) { - if (_can_drop (_, *unicodes_set, base, unicodes_cache, + it | hb_map (hb_first), encodingrec_iter)) continue; + if (_can_drop (_, + *unicodes_set, + base, + *unicodes_cache, + local_unicodes_cache, + + it | hb_map (hb_first), encodingrec_iter)) + continue; c->copy (_, + it | hb_filter (*unicodes_set, hb_first), 12u, base, plan, &format12objidx); } else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx); @@ -1585,7 +1702,8 @@ struct cmap bool _can_drop (const EncodingRecord& cmap12, const hb_set_t& cmap12_unicodes, const void* base, - SubtableUnicodesCache& unicodes_cache, + const SubtableUnicodesCache& unicodes_cache, + SubtableUnicodesCache& local_unicodes_cache, Iterator subset_unicodes, EncodingRecordIterator encoding_records) { @@ -1616,7 +1734,7 @@ struct cmap || (base+_.subtable).get_language() != target_language) continue; - hb_set_t* sibling_unicodes = unicodes_cache.set_for (&_); + const hb_set_t* sibling_unicodes = unicodes_cache.set_for (&_, local_unicodes_cache); auto cmap12 = + subset_unicodes | hb_filter (cmap12_unicodes); auto sibling = + subset_unicodes | hb_filter (*sibling_unicodes); @@ -1653,17 +1771,9 @@ struct cmap auto encodingrec_iter = + hb_iter (encodingRecord) - | hb_filter ([&] (const EncodingRecord& _) - { - if ((_.platformID == 0 && _.encodingID == 3) || - (_.platformID == 0 && _.encodingID == 4) || - (_.platformID == 3 && _.encodingID == 1) || - (_.platformID == 3 && _.encodingID == 10) || - (this + _.subtable).u.format == 14) - return true; - - return false; - }) + | hb_filter ([&](const EncodingRecord& _) { + return cmap::filter_encoding_records_for_subset (this, _); + }) ; if (unlikely (!encodingrec_iter.len ())) return_trace (false); @@ -1692,7 +1802,11 @@ struct cmap { return (_.second != HB_MAP_VALUE_INVALID); }) ; - return_trace (cmap_prime->serialize (c->serializer, it, encodingrec_iter, this, c->plan)); + return_trace (cmap_prime->serialize (c->serializer, + it, + encodingrec_iter, + this, + c->plan)); } const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const @@ -1728,6 +1842,8 @@ struct cmap struct accelerator_t { + using cache_t = hb_cache_t<21, 16, 8, true>; + accelerator_t (hb_face_t *face) { this->table = hb_sanitize_context_t ().reference_table (face); @@ -1782,26 +1898,43 @@ struct cmap } ~accelerator_t () { this->table.destroy (); } + inline bool _cached_get (hb_codepoint_t unicode, + hb_codepoint_t *glyph, + cache_t *cache) const + { + unsigned v; + if (cache && cache->get (unicode, &v)) + { + *glyph = v; + return true; + } + bool ret = this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph); + + if (cache && ret) + cache->set (unicode, *glyph); + return ret; + } + bool get_nominal_glyph (hb_codepoint_t unicode, - hb_codepoint_t *glyph) const + hb_codepoint_t *glyph, + cache_t *cache = nullptr) const { - if (unlikely (!this->get_glyph_funcZ)) return false; - return this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph); + if (unlikely (!this->get_glyph_funcZ)) return 0; + return _cached_get (unicode, glyph, cache); } + unsigned int get_nominal_glyphs (unsigned int count, const hb_codepoint_t *first_unicode, unsigned int unicode_stride, hb_codepoint_t *first_glyph, - unsigned int glyph_stride) const + unsigned int glyph_stride, + cache_t *cache = nullptr) const { if (unlikely (!this->get_glyph_funcZ)) return 0; - hb_cmap_get_glyph_func_t get_glyph_funcZ = this->get_glyph_funcZ; - const void *get_glyph_data = this->get_glyph_data; - unsigned int done; for (done = 0; - done < count && get_glyph_funcZ (get_glyph_data, *first_unicode, first_glyph); + done < count && _cached_get (*first_unicode, first_glyph, cache); done++) { first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride); @@ -1812,7 +1945,8 @@ struct cmap bool get_variation_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector, - hb_codepoint_t *glyph) const + hb_codepoint_t *glyph, + cache_t *cache = nullptr) const { switch (this->subtable_uvs->get_glyph_variant (unicode, variation_selector, @@ -1823,7 +1957,7 @@ struct cmap case GLYPH_VARIANT_USE_DEFAULT: break; } - return get_nominal_glyph (unicode, glyph); + return get_nominal_glyph (unicode, glyph, cache); } void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const @@ -1928,6 +2062,19 @@ struct cmap encodingRecord.sanitize (c, this)); } + private: + + static bool filter_encoding_records_for_subset(const cmap* cmap, + const EncodingRecord& _) + { + return + (_.platformID == 0 && _.encodingID == 3) || + (_.platformID == 0 && _.encodingID == 4) || + (_.platformID == 3 && _.encodingID == 1) || + (_.platformID == 3 && _.encodingID == 10) || + (cmap + _.subtable).u.format == 14; + } + protected: HBUINT16 version; /* Table version number (0). */ SortedArray16Of diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc index 5b45dbcd9fc..ef46fe5429a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc @@ -31,11 +31,11 @@ #include "hb-ot.h" -#include "hb-ot-color-cbdt-table.hh" -#include "hb-ot-color-colr-table.hh" -#include "hb-ot-color-cpal-table.hh" -#include "hb-ot-color-sbix-table.hh" -#include "hb-ot-color-svg-table.hh" +#include "OT/Color/CBDT/CBDT.hh" +#include "OT/Color/COLR/COLR.hh" +#include "OT/Color/CPAL/CPAL.hh" +#include "OT/Color/sbix/sbix.hh" +#include "OT/Color/svg/svg.hh" /** @@ -61,7 +61,7 @@ * * Tests whether a face includes a `CPAL` color-palette table. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 2.1.0 */ @@ -167,6 +167,10 @@ hb_ot_color_palette_get_flags (hb_face_t *face, * for allocating a buffer of suitable size before calling * hb_ot_color_palette_get_colors() a second time. * + * The RGBA values in the palette are unpremultiplied. See the + * OpenType spec [CPAL](https://learn.microsoft.com/en-us/typography/opentype/spec/cpal) + * section for details. + * * Return value: the total number of colors in the palette * * Since: 2.1.0 @@ -190,16 +194,53 @@ hb_ot_color_palette_get_colors (hb_face_t *face, * hb_ot_color_has_layers: * @face: #hb_face_t to work upon * - * Tests whether a face includes any `COLR` color layers. + * Tests whether a face includes a `COLR` table + * with data according to COLRv0. * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 2.1.0 */ hb_bool_t hb_ot_color_has_layers (hb_face_t *face) { - return face->table.COLR->has_data (); + return face->table.COLR->has_v0_data (); +} + +/** + * hb_ot_color_has_paint: + * @face: #hb_face_t to work upon + * + * Tests where a face includes a `COLR` table + * with data according to COLRv1. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 7.0.0 + */ +hb_bool_t +hb_ot_color_has_paint (hb_face_t *face) +{ + return face->table.COLR->has_v1_data (); +} + +/** + * hb_ot_color_glyph_has_paint: + * @face: #hb_face_t to work upon + * @glyph: The glyph index to query + * + * Tests where a face includes COLRv1 paint + * data for @glyph. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 7.0.0 + */ +hb_bool_t +hb_ot_color_glyph_has_paint (hb_face_t *face, + hb_codepoint_t glyph) +{ + return face->table.COLR->has_paint_for_glyph (glyph); } /** @@ -239,7 +280,7 @@ hb_ot_color_glyph_get_layers (hb_face_t *face, * * Tests whether a face includes any `SVG` glyph images. * - * Return value: %true if data found, %false otherwise. + * Return value: `true` if data found, `false` otherwise. * * Since: 2.1.0 */ @@ -279,7 +320,7 @@ hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph) * * Tests whether a face has PNG glyph images (either in `CBDT` or `sbix` tables). * - * Return value: %true if data found, %false otherwise + * Return value: `true` if data found, `false` otherwise * * Since: 2.1.0 */ @@ -295,8 +336,8 @@ hb_ot_color_has_png (hb_face_t *face) * @glyph: a glyph index * * Fetches the PNG image for a glyph. This function takes a font object, not a face object, - * as input. To get an optimally sized PNG blob, the UPEM value must be set on the @font - * object. If UPEM is unset, the blob returned will be the largest PNG available. + * as input. To get an optimally sized PNG blob, the PPEM values must be set on the @font + * object. If PPEM is unset, the blob returned will be the largest PNG available. * * If the glyph has no PNG image, the singleton empty blob is returned. * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h index ff659c7a458..7fa810bd96f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h @@ -102,6 +102,10 @@ hb_ot_color_has_layers (hb_face_t *face); * * Pairs of glyph and color index. * + * A color index of 0xFFFF does not refer to a palette + * color, but indicates that the foreground color should + * be used. + * * Since: 2.1.0 **/ typedef struct hb_ot_color_layer_t { @@ -116,6 +120,15 @@ hb_ot_color_glyph_get_layers (hb_face_t *face, unsigned int *layer_count, /* IN/OUT. May be NULL. */ hb_ot_color_layer_t *layers /* OUT. May be NULL. */); +/* COLRv1 */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_paint (hb_face_t *face); + +HB_EXTERN hb_bool_t +hb_ot_color_glyph_has_paint (hb_face_t *face, + hb_codepoint_t glyph); + /* * SVG */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h index da205d6713d..24656af53bb 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h @@ -67,26 +67,30 @@ HB_BEGIN_DECLS /* Like hb_ot_layout_table_find_script, but takes zero-terminated array of scripts to test */ -HB_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_table_select_script) hb_bool_t +HB_DEPRECATED_FOR (hb_ot_layout_table_select_script) +HB_EXTERN hb_bool_t hb_ot_layout_table_choose_script (hb_face_t *face, hb_tag_t table_tag, const hb_tag_t *script_tags, unsigned int *script_index, hb_tag_t *chosen_script); -HB_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_script_select_language) hb_bool_t +HB_DEPRECATED_FOR (hb_ot_layout_script_select_language) +HB_EXTERN hb_bool_t hb_ot_layout_script_find_language (hb_face_t *face, hb_tag_t table_tag, unsigned int script_index, hb_tag_t language_tag, unsigned int *language_index); -HB_EXTERN HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) void +HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) +HB_EXTERN void hb_ot_tags_from_script (hb_script_t script, hb_tag_t *script_tag_1, hb_tag_t *script_tag_2); -HB_EXTERN HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) hb_tag_t +HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) +HB_EXTERN hb_tag_t hb_ot_tag_from_language (hb_language_t language); @@ -121,13 +125,15 @@ typedef struct hb_ot_var_axis_t { float max_value; } hb_ot_var_axis_t; -HB_EXTERN HB_DEPRECATED_FOR (hb_ot_var_get_axis_infos) unsigned int +HB_DEPRECATED_FOR (hb_ot_var_get_axis_infos) +HB_EXTERN unsigned int hb_ot_var_get_axes (hb_face_t *face, unsigned int start_offset, unsigned int *axes_count /* IN/OUT */, hb_ot_var_axis_t *axes_array /* OUT */); -HB_EXTERN HB_DEPRECATED_FOR (hb_ot_var_find_axis_info) hb_bool_t +HB_DEPRECATED_FOR (hb_ot_var_find_axis_info) +HB_EXTERN hb_bool_t hb_ot_var_find_axis (hb_face_t *face, hb_tag_t axis_tag, unsigned int *axis_index, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh index c05034b3bb5..c9da36c1bb5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh @@ -56,9 +56,9 @@ HB_OT_CORE_TABLE (OT, maxp) #if !defined(HB_NO_FACE_COLLECT_UNICODES) || !defined(HB_NO_OT_FONT) HB_OT_ACCELERATOR (OT, cmap) #endif -HB_OT_TABLE (OT, hhea) +HB_OT_CORE_TABLE (OT, hhea) HB_OT_ACCELERATOR (OT, hmtx) -HB_OT_TABLE (OT, OS2) +HB_OT_CORE_TABLE (OT, OS2) #if !defined(HB_NO_OT_FONT_GLYPH_NAMES) || !defined(HB_NO_METRICS) || !defined(HB_NO_STYLE) HB_OT_ACCELERATOR (OT, post) #endif @@ -66,7 +66,7 @@ HB_OT_ACCELERATOR (OT, post) HB_OT_ACCELERATOR (OT, name) #endif #ifndef HB_NO_STYLE -HB_OT_TABLE (OT, STAT) +HB_OT_CORE_TABLE (OT, STAT) #endif #ifndef HB_NO_META HB_OT_ACCELERATOR (OT, meta) @@ -74,9 +74,9 @@ HB_OT_ACCELERATOR (OT, meta) /* Vertical layout. */ #ifndef HB_NO_VERTICAL -HB_OT_TABLE (OT, vhea) +HB_OT_CORE_TABLE (OT, vhea) HB_OT_ACCELERATOR (OT, vmtx) -HB_OT_TABLE (OT, VORG) +HB_OT_CORE_TABLE (OT, VORG) #endif /* TrueType outlines. */ @@ -91,15 +91,15 @@ HB_OT_ACCELERATOR (OT, cff2) /* OpenType variations. */ #ifndef HB_NO_VAR -HB_OT_TABLE (OT, fvar) -HB_OT_TABLE (OT, avar) +HB_OT_CORE_TABLE (OT, fvar) +HB_OT_CORE_TABLE (OT, avar) HB_OT_ACCELERATOR (OT, gvar) -HB_OT_TABLE (OT, MVAR) +HB_OT_CORE_TABLE (OT, MVAR) #endif /* Legacy kern. */ #ifndef HB_NO_OT_KERN -HB_OT_TABLE (OT, kern) +HB_OT_CORE_TABLE (OT, kern) #endif /* OpenType shaping. */ @@ -107,12 +107,12 @@ HB_OT_TABLE (OT, kern) HB_OT_ACCELERATOR (OT, GDEF) HB_OT_ACCELERATOR (OT, GSUB) HB_OT_ACCELERATOR (OT, GPOS) -//HB_OT_TABLE (OT, JSTF) +//HB_OT_CORE_TABLE (OT, JSTF) #endif /* OpenType baseline. */ #ifndef HB_NO_BASE -HB_OT_TABLE (OT, BASE) +HB_OT_CORE_TABLE (OT, BASE) #endif /* AAT shaping. */ @@ -129,8 +129,8 @@ HB_OT_TABLE (AAT, feat) /* OpenType color fonts. */ #ifndef HB_NO_COLOR -HB_OT_TABLE (OT, COLR) -HB_OT_TABLE (OT, CPAL) +HB_OT_CORE_TABLE (OT, COLR) +HB_OT_CORE_TABLE (OT, CPAL) HB_OT_ACCELERATOR (OT, CBDT) HB_OT_ACCELERATOR (OT, sbix) HB_OT_ACCELERATOR (OT, SVG) @@ -138,7 +138,7 @@ HB_OT_ACCELERATOR (OT, SVG) /* OpenType math. */ #ifndef HB_NO_MATH -HB_OT_TABLE (OT, MATH) +HB_OT_CORE_TABLE (OT, MATH) #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc index 5ef8df43ce7..2243ee02874 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc @@ -35,9 +35,9 @@ #include "hb-ot-meta-table.hh" #include "hb-ot-name-table.hh" #include "hb-ot-post-table.hh" -#include "hb-ot-color-cbdt-table.hh" -#include "hb-ot-color-sbix-table.hh" -#include "hb-ot-color-svg-table.hh" +#include "OT/Color/CBDT/CBDT.hh" +#include "OT/Color/sbix/sbix.hh" +#include "OT/Color/svg/svg.hh" #include "hb-ot-layout-gdef-table.hh" #include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gpos-table.hh" diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc index 2f3d4759118..f1e882ed938 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc @@ -34,18 +34,20 @@ #include "hb-font.hh" #include "hb-machinery.hh" #include "hb-ot-face.hh" +#include "hb-outline.hh" #include "hb-ot-cmap-table.hh" #include "hb-ot-glyf-table.hh" #include "hb-ot-cff1-table.hh" #include "hb-ot-cff2-table.hh" #include "hb-ot-hmtx-table.hh" -#include "hb-ot-os2-table.hh" #include "hb-ot-post-table.hh" #include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise. #include "hb-ot-vorg-table.hh" -#include "hb-ot-color-cbdt-table.hh" -#include "hb-ot-color-sbix-table.hh" +#include "OT/Color/CBDT/CBDT.hh" +#include "OT/Color/COLR/COLR.hh" +#include "OT/Color/sbix/sbix.hh" +#include "OT/Color/svg/svg.hh" /** @@ -59,13 +61,20 @@ * never need to call these functions directly. **/ +using hb_ot_font_cmap_cache_t = hb_cache_t<21, 16, 8, true>; +using hb_ot_font_advance_cache_t = hb_cache_t<24, 16, 8, true>; + +static hb_user_data_key_t hb_ot_font_cmap_cache_user_data_key; + struct hb_ot_font_t { const hb_ot_face_t *ot_face; + hb_ot_font_cmap_cache_t *cmap_cache; + /* h_advance caching */ mutable hb_atomic_int_t cached_coords_serial; - mutable hb_atomic_ptr_t advance_cache; + mutable hb_atomic_ptr_t advance_cache; }; static hb_ot_font_t * @@ -77,6 +86,33 @@ _hb_ot_font_create (hb_font_t *font) ot_font->ot_face = &font->face->table; + // retry: + auto *cmap_cache = (hb_ot_font_cmap_cache_t *) hb_face_get_user_data (font->face, + &hb_ot_font_cmap_cache_user_data_key); + if (!cmap_cache) + { + cmap_cache = (hb_ot_font_cmap_cache_t *) hb_malloc (sizeof (hb_ot_font_cmap_cache_t)); + if (unlikely (!cmap_cache)) goto out; + cmap_cache->init (); + if (unlikely (!hb_face_set_user_data (font->face, + &hb_ot_font_cmap_cache_user_data_key, + cmap_cache, + hb_free, + false))) + { + hb_free (cmap_cache); + cmap_cache = nullptr; + /* Normally we would retry here, but that would + * infinite-loop if the face is the empty-face. + * Just let it go and this font will be uncached if it + * happened to collide with another thread creating the + * cache at the same time. */ + // goto retry; + } + } + out: + ot_font->cmap_cache = cmap_cache; + return ot_font; } @@ -86,11 +122,7 @@ _hb_ot_font_destroy (void *font_data) hb_ot_font_t *ot_font = (hb_ot_font_t *) font_data; auto *cache = ot_font->advance_cache.get_relaxed (); - if (cache) - { - cache->fini (); - hb_free (cache); - } + hb_free (cache); hb_free (ot_font); } @@ -104,7 +136,7 @@ hb_ot_get_nominal_glyph (hb_font_t *font HB_UNUSED, { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; const hb_ot_face_t *ot_face = ot_font->ot_face; - return ot_face->cmap->get_nominal_glyph (unicode, glyph); + return ot_face->cmap->get_nominal_glyph (unicode, glyph, ot_font->cmap_cache); } static unsigned int @@ -121,7 +153,8 @@ hb_ot_get_nominal_glyphs (hb_font_t *font HB_UNUSED, const hb_ot_face_t *ot_face = ot_font->ot_face; return ot_face->cmap->get_nominal_glyphs (count, first_unicode, unicode_stride, - first_glyph, glyph_stride); + first_glyph, glyph_stride, + ot_font->cmap_cache); } static hb_bool_t @@ -134,7 +167,9 @@ hb_ot_get_variation_glyph (hb_font_t *font HB_UNUSED, { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; const hb_ot_face_t *ot_face = ot_font->ot_face; - return ot_face->cmap->get_variation_glyph (unicode, variation_selector, glyph); + return ot_face->cmap->get_variation_glyph (unicode, + variation_selector, glyph, + ot_font->cmap_cache); } static void @@ -146,12 +181,15 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, unsigned advance_stride, void *user_data HB_UNUSED) { + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; const hb_ot_face_t *ot_face = ot_font->ot_face; const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx; + hb_position_t *orig_first_advance = first_advance; + #ifndef HB_NO_VAR - const OT::HVARVVAR &HVAR = *hmtx.var_table; + const OT::HVAR &HVAR = *hmtx.var_table; const OT::VariationStore &varStore = &HVAR + HVAR.varStore; OT::VariationStore::cache_t *varStore_cache = font->num_coords * count >= 128 ? varStore.create_cache () : nullptr; @@ -161,14 +199,14 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, bool use_cache = false; #endif - hb_advance_cache_t *cache = nullptr; + hb_ot_font_advance_cache_t *cache = nullptr; if (use_cache) { retry: - cache = ot_font->advance_cache.get (); + cache = ot_font->advance_cache.get_acquire (); if (unlikely (!cache)) { - cache = (hb_advance_cache_t *) hb_malloc (sizeof (hb_advance_cache_t)); + cache = (hb_ot_font_advance_cache_t *) hb_malloc (sizeof (hb_ot_font_advance_cache_t)); if (unlikely (!cache)) { use_cache = false; @@ -181,7 +219,7 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, hb_free (cache); goto retry; } - ot_font->cached_coords_serial.set (font->serial_coords); + ot_font->cached_coords_serial.set_release (font->serial_coords); } } out: @@ -190,17 +228,17 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, { for (unsigned int i = 0; i < count; i++) { - *first_advance = font->em_scale_x (hmtx.get_advance (*first_glyph, font, varStore_cache)); + *first_advance = font->em_scale_x (hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache)); first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } } else { /* Use cache. */ - if (ot_font->cached_coords_serial.get () != (int) font->serial_coords) + if (ot_font->cached_coords_serial.get_acquire () != (int) font->serial_coords) { ot_font->advance_cache->init (); - ot_font->cached_coords_serial.set (font->serial_coords); + ot_font->cached_coords_serial.set_release (font->serial_coords); } for (unsigned int i = 0; i < count; i++) @@ -211,7 +249,7 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, v = cv; else { - v = hmtx.get_advance (*first_glyph, font, varStore_cache); + v = hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache); ot_font->advance_cache->set (*first_glyph, v); } *first_advance = font->em_scale_x (v); @@ -223,6 +261,18 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, #ifndef HB_NO_VAR OT::VariationStore::destroy_cache (varStore_cache); #endif + + if (font->x_strength && !font->embolden_in_place) + { + /* Emboldening. */ + hb_position_t x_strength = font->x_scale >= 0 ? font->x_strength : -font->x_strength; + first_advance = orig_first_advance; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? x_strength : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } } #ifndef HB_NO_VERTICAL @@ -239,10 +289,12 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, const hb_ot_face_t *ot_face = ot_font->ot_face; const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; + hb_position_t *orig_first_advance = first_advance; + if (vmtx.has_data ()) { #ifndef HB_NO_VAR - const OT::HVARVVAR &VVAR = *vmtx.var_table; + const OT::VVAR &VVAR = *vmtx.var_table; const OT::VariationStore &varStore = &VVAR + VVAR.varStore; OT::VariationStore::cache_t *varStore_cache = font->num_coords ? varStore.create_cache () : nullptr; #else @@ -251,7 +303,7 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, for (unsigned int i = 0; i < count; i++) { - *first_advance = font->em_scale_y (-(int) vmtx.get_advance (*first_glyph, font, varStore_cache)); + *first_advance = font->em_scale_y (-(int) vmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache)); first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } @@ -273,6 +325,18 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } } + + if (font->y_strength && !font->embolden_in_place) + { + /* Emboldening. */ + hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength; + first_advance = orig_first_advance; + for (unsigned int i = 0; i < count; i++) + { + *first_advance += *first_advance ? y_strength : 0; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } } #endif @@ -293,17 +357,28 @@ hb_ot_get_glyph_v_origin (hb_font_t *font, const OT::VORG &VORG = *ot_face->VORG; if (VORG.has_data ()) { - *y = font->em_scale_y (VORG.get_y_origin (glyph)); + float delta = 0; + +#ifndef HB_NO_VAR + const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; + const OT::VVAR &VVAR = *vmtx.var_table; + if (font->num_coords) + VVAR.get_vorg_delta_unscaled (glyph, + font->coords, font->num_coords, + &delta); +#endif + + *y = font->em_scalef_y (VORG.get_y_origin (glyph) + delta); return true; } hb_glyph_extents_t extents = {0}; if (ot_face->glyf->get_extents (font, glyph, &extents)) { - if (ot_face->vmtx->has_data ()) + const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; + int tsb = 0; + if (vmtx.get_leading_bearing_with_var_unscaled (font, glyph, &tsb)) { - const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; - hb_position_t tsb = vmtx.get_side_bearing (font, glyph); *y = extents.y_bearing + font->em_scale_y (tsb); return true; } @@ -336,17 +411,17 @@ hb_ot_get_glyph_extents (hb_font_t *font, #if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR) if (ot_face->sbix->get_extents (font, glyph, extents)) return true; + if (ot_face->CBDT->get_extents (font, glyph, extents)) return true; +#endif +#if !defined(HB_NO_COLOR) + if (ot_face->COLR->get_extents (font, glyph, extents)) return true; #endif if (ot_face->glyf->get_extents (font, glyph, extents)) return true; #ifndef HB_NO_OT_FONT_CFF if (ot_face->cff1->get_extents (font, glyph, extents)) return true; if (ot_face->cff2->get_extents (font, glyph, extents)) return true; #endif -#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR) - if (ot_face->CBDT->get_extents (font, glyph, extents)) return true; -#endif - // TODO Hook up side-bearings variations. return false; } @@ -391,9 +466,16 @@ hb_ot_get_font_h_extents (hb_font_t *font, hb_font_extents_t *metrics, void *user_data HB_UNUSED) { - return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &metrics->ascender) && - _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &metrics->descender) && - _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &metrics->line_gap); + bool ret = _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &metrics->line_gap); + + /* Embolden */ + int y_shift = font->y_strength; + if (font->y_scale < 0) y_shift = -y_shift; + metrics->ascender += y_shift; + + return ret; } #ifndef HB_NO_VERTICAL @@ -411,17 +493,62 @@ hb_ot_get_font_v_extents (hb_font_t *font, #ifndef HB_NO_DRAW static void -hb_ot_get_glyph_shape (hb_font_t *font, - void *font_data HB_UNUSED, - hb_codepoint_t glyph, - hb_draw_funcs_t *draw_funcs, void *draw_data, - void *user_data) +hb_ot_draw_glyph (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data) { - hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy); - if (font->face->table.glyf->get_path (font, glyph, draw_session)) return; + bool embolden = font->x_strength || font->y_strength; + hb_outline_t outline; + + { // Need draw_session to be destructed before emboldening. + hb_draw_session_t draw_session (embolden ? hb_outline_recording_pen_get_funcs () : draw_funcs, + embolden ? &outline : draw_data, font->slant_xy); + if (!font->face->table.glyf->get_path (font, glyph, draw_session)) #ifndef HB_NO_CFF - if (font->face->table.cff1->get_path (font, glyph, draw_session)) return; - if (font->face->table.cff2->get_path (font, glyph, draw_session)) return; + if (!font->face->table.cff1->get_path (font, glyph, draw_session)) + if (!font->face->table.cff2->get_path (font, glyph, draw_session)) +#endif + {} + } + + if (embolden) + { + float x_shift = font->embolden_in_place ? 0 : (float) font->x_strength / 2; + float y_shift = (float) font->y_strength / 2; + if (font->x_scale < 0) x_shift = -x_shift; + if (font->y_scale < 0) y_shift = -y_shift; + outline.embolden (font->x_strength, font->y_strength, + x_shift, y_shift); + + outline.replay (draw_funcs, draw_data); + } +} +#endif + +#ifndef HB_NO_PAINT +static void +hb_ot_paint_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_paint_funcs_t *paint_funcs, void *paint_data, + unsigned int palette, + hb_color_t foreground, + void *user_data) +{ +#ifndef HB_NO_COLOR + if (font->face->table.COLR->paint_glyph (font, glyph, paint_funcs, paint_data, palette, foreground)) return; + if (font->face->table.SVG->paint_glyph (font, glyph, paint_funcs, paint_data)) return; +#ifndef HB_NO_OT_FONT_BITMAP + if (font->face->table.CBDT->paint_glyph (font, glyph, paint_funcs, paint_data)) return; + if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return; +#endif +#endif + if (font->face->table.glyf->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; +#ifndef HB_NO_CFF + if (font->face->table.cff1->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; + if (font->face->table.cff2->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; #endif } #endif @@ -449,7 +576,11 @@ static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_tface->table.glyf->get_side_bearing_var (font, glyph, is_vertical); + return font->face->table.glyf->get_leading_bearing_with_var_unscaled (font, glyph, is_vertical, lsb); } unsigned -_glyf_get_advance_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical) +_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical) { - return font->face->table.glyf->get_advance_var (font, glyph, is_vertical); + return font->face->table.glyf->get_advance_with_var_unscaled (font, glyph, is_vertical); } #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh index 84154467ed1..9346641ac35 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh @@ -156,6 +156,7 @@ struct hdmx TRACE_SANITIZE (this); return_trace (c->check_struct (this) && !hb_unsigned_mul_overflows (numRecords, sizeDeviceRecord) && + min_size + numRecords * sizeDeviceRecord > numRecords * sizeDeviceRecord && sizeDeviceRecord >= DeviceRecord::min_size && c->check_range (this, get_size ())); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh index c87356ae16c..22bd5fa255e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh @@ -97,6 +97,7 @@ struct head * entire font as HBUINT32, then store * 0xB1B0AFBAu - sum. */ HBUINT32 magicNumber; /* Set to 0x5F0F3CF5u. */ + public: HBUINT16 flags; /* Bit 0: Baseline for font at y=0; * Bit 1: Left sidebearing point at x=0; * Bit 2: Instructions may depend on point size; @@ -141,6 +142,7 @@ struct head * encoded in the cmap subtables represent proper * support for those code points. * Bit 15: Reserved, set to 0. */ + protected: HBUINT16 unitsPerEm; /* Valid range is from 16 to 16384. This value * should be a power of 2 for fonts that have * TrueType outlines. */ @@ -148,10 +150,12 @@ struct head January 1, 1904. 64-bit integer */ LONGDATETIME modified; /* Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer */ + public: HBINT16 xMin; /* For all glyph bounding boxes. */ HBINT16 yMin; /* For all glyph bounding boxes. */ HBINT16 xMax; /* For all glyph bounding boxes. */ HBINT16 yMax; /* For all glyph bounding boxes. */ + protected: HBUINT16 macStyle; /* Bit 0: Bold (if set to 1); * Bit 1: Italic (if set to 1) * Bit 2: Underline (if set to 1) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh index 9883df20860..4ef88053f29 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh @@ -31,6 +31,7 @@ #include "hb-ot-maxp-table.hh" #include "hb-ot-hhea-table.hh" #include "hb-ot-var-hvar-table.hh" +#include "hb-ot-var-mvar-table.hh" #include "hb-ot-metrics.hh" /* @@ -43,11 +44,11 @@ #define HB_OT_TAG_vmtx HB_TAG('v','m','t','x') -HB_INTERNAL int -_glyf_get_side_bearing_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical); +HB_INTERNAL bool +_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical, int *lsb); HB_INTERNAL unsigned -_glyf_get_advance_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical); +_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical); namespace OT { @@ -62,7 +63,7 @@ struct LongMetric }; -template +template struct hmtxvmtx { bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const @@ -73,11 +74,15 @@ struct hmtxvmtx return_trace (true); } + const hb_hashmap_t>* get_mtx_map (const hb_subset_plan_t *plan) const + { return T::is_horizontal ? &plan->hmtx_map : &plan->vmtx_map; } - bool subset_update_header (hb_subset_plan_t *plan, - unsigned int num_hmetrics) const + bool subset_update_header (hb_subset_context_t *c, + unsigned int num_hmetrics, + const hb_hashmap_t> *mtx_map, + const hb_map_t *bounds_map) const { - hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table (plan->source, H::tableTag); + hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table (c->plan->source, H::tableTag); hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail (src_blob); hb_blob_destroy (src_blob); @@ -89,7 +94,56 @@ struct hmtxvmtx H *table = (H *) hb_blob_get_data (dest_blob, &length); table->numberOfLongMetrics = num_hmetrics; - bool result = plan->add_table (H::tableTag, dest_blob); +#ifndef HB_NO_VAR + if (c->plan->normalized_coords) + { + auto &MVAR = *c->plan->source->table.MVAR; + if (T::is_horizontal) + { + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE, caretSlopeRise); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN, caretSlopeRun); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET, caretOffset); + } + else + { + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_VERTICAL_CARET_RISE, caretSlopeRise); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_VERTICAL_CARET_RUN, caretSlopeRun); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET, caretOffset); + } + + int min_lsb = 0x7FFF; + int min_rsb = 0x7FFF; + int max_extent = -0x7FFF; + unsigned max_adv = 0; + for (const auto _ : *mtx_map) + { + hb_codepoint_t gid = _.first; + unsigned adv = _.second.first; + int lsb = _.second.second; + max_adv = hb_max (max_adv, adv); + + if (bounds_map->has (gid)) + { + unsigned bound_width = bounds_map->get (gid); + int rsb = adv - lsb - bound_width; + int extent = lsb + bound_width; + min_lsb = hb_min (min_lsb, lsb); + min_rsb = hb_min (min_rsb, rsb); + max_extent = hb_max (max_extent, extent); + } + } + + table->advanceMax = max_adv; + if (!bounds_map->is_empty ()) + { + table->minLeadingBearing = min_lsb; + table->minTrailingBearing = min_rsb; + table->maxExtent = max_extent; + } + } +#endif + + bool result = c->plan->add_table (H::tableTag, dest_blob); hb_blob_destroy (dest_blob); return result; @@ -130,14 +184,15 @@ struct hmtxvmtx accelerator_t _mtx (c->plan->source); unsigned num_long_metrics; + const hb_hashmap_t> *mtx_map = get_mtx_map (c->plan); { /* Determine num_long_metrics to encode. */ auto& plan = c->plan; + num_long_metrics = plan->num_output_glyphs (); - hb_codepoint_t old_gid = 0; - unsigned int last_advance = plan->old_gid_for_new_gid (num_long_metrics - 1, &old_gid) ? _mtx.get_advance (old_gid) : 0; + unsigned int last_advance = get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 1, _mtx); while (num_long_metrics > 1 && - last_advance == (plan->old_gid_for_new_gid (num_long_metrics - 2, &old_gid) ? _mtx.get_advance (old_gid) : 0)) + last_advance == get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 2, _mtx)) { num_long_metrics--; } @@ -145,12 +200,18 @@ struct hmtxvmtx auto it = + hb_range (c->plan->num_output_glyphs ()) - | hb_map ([c, &_mtx] (unsigned _) + | hb_map ([c, &_mtx, mtx_map] (unsigned _) { - hb_codepoint_t old_gid; - if (!c->plan->old_gid_for_new_gid (_, &old_gid)) - return hb_pair (0u, 0); - return hb_pair (_mtx.get_advance (old_gid), _mtx.get_side_bearing (old_gid)); + if (!mtx_map->has (_)) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (_, &old_gid)) + return hb_pair (0u, 0); + int lsb = 0; + (void) _mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb); + return hb_pair (_mtx.get_advance_without_var_unscaled (old_gid), +lsb); + } + return mtx_map->get (_); }) ; @@ -160,7 +221,8 @@ struct hmtxvmtx return_trace (false); // Amend header num hmetrics - if (unlikely (!subset_update_header (c->plan, num_long_metrics))) + if (unlikely (!subset_update_header (c, num_long_metrics, mtx_map, + T::is_horizontal ? &c->plan->bounds_width_map : &c->plan->bounds_height_map))) return_trace (false); return_trace (true); @@ -173,7 +235,7 @@ struct hmtxvmtx accelerator_t (hb_face_t *face) { table = hb_sanitize_context_t ().reference_table (face, T::tableTag); - var_table = hb_sanitize_context_t ().reference_table (face, T::variationsTag); + var_table = hb_sanitize_context_t ().reference_table (face, T::variationsTag); default_advance = T::is_horizontal ? hb_face_get_upem (face) / 2 : hb_face_get_upem (face); @@ -221,36 +283,46 @@ struct hmtxvmtx bool has_data () const { return (bool) num_bearings; } - int get_side_bearing (hb_codepoint_t glyph) const + bool get_leading_bearing_without_var_unscaled (hb_codepoint_t glyph, + int *lsb) const { if (glyph < num_long_metrics) - return table->longMetricZ[glyph].sb; + { + *lsb = table->longMetricZ[glyph].sb; + return true; + } if (unlikely (glyph >= num_bearings)) - return 0; + return false; const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics]; - return bearings[glyph - num_long_metrics]; + *lsb = bearings[glyph - num_long_metrics]; + return true; } - int get_side_bearing (hb_font_t *font, hb_codepoint_t glyph) const + bool get_leading_bearing_with_var_unscaled (hb_font_t *font, + hb_codepoint_t glyph, + int *lsb) const { - int side_bearing = get_side_bearing (glyph); + if (!font->num_coords) + return get_leading_bearing_without_var_unscaled (glyph, lsb); #ifndef HB_NO_VAR - if (unlikely (glyph >= num_bearings) || !font->num_coords) - return side_bearing; - - if (var_table.get_length ()) - return side_bearing + var_table->get_side_bearing_var (glyph, font->coords, font->num_coords); + float delta; + if (var_table->get_lsb_delta_unscaled (glyph, font->coords, font->num_coords, &delta) && + get_leading_bearing_without_var_unscaled (glyph, lsb)) + { + *lsb += roundf (delta); + return true; + } - return _glyf_get_side_bearing_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx); + return _glyf_get_leading_bearing_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx, lsb); #else - return side_bearing; + return false; #endif } - unsigned int get_advance (hb_codepoint_t glyph) const + unsigned int get_advance_without_var_unscaled (hb_codepoint_t glyph) const { /* OpenType case. */ if (glyph < num_bearings) @@ -262,7 +334,7 @@ struct hmtxvmtx if (unlikely (!num_advances)) return default_advance; -#ifdef HB_NO_BORING_EXPANSION +#ifdef HB_NO_BEYOND_64K return 0; #endif @@ -272,10 +344,8 @@ struct hmtxvmtx /* num_bearings <= glyph < num_glyphs; * num_bearings <= num_advances */ - /* TODO Optimize */ - if (num_bearings == num_advances) - return get_advance (num_bearings - 1); + return get_advance_without_var_unscaled (num_bearings - 1); const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics]; const UFWORD *advances = (const UFWORD *) &bearings[num_bearings - num_long_metrics]; @@ -283,20 +353,22 @@ struct hmtxvmtx return advances[hb_min (glyph - num_bearings, num_advances - num_bearings - 1)]; } - unsigned int get_advance (hb_codepoint_t glyph, - hb_font_t *font, - VariationStore::cache_t *store_cache = nullptr) const + unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph, + hb_font_t *font, + VariationStore::cache_t *store_cache = nullptr) const { - unsigned int advance = get_advance (glyph); + unsigned int advance = get_advance_without_var_unscaled (glyph); #ifndef HB_NO_VAR if (unlikely (glyph >= num_bearings) || !font->num_coords) return advance; if (var_table.get_length ()) - return advance + roundf (var_table->get_advance_var (glyph, font, store_cache)); // TODO Optimize?! + return advance + roundf (var_table->get_advance_delta_unscaled (glyph, + font->coords, font->num_coords, + store_cache)); - return _glyf_get_advance_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx); + return _glyf_get_advance_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx); #else return advance; #endif @@ -313,9 +385,25 @@ struct hmtxvmtx public: hb_blob_ptr_t table; - hb_blob_ptr_t var_table; + hb_blob_ptr_t var_table; }; + /* get advance: when no variations, call get_advance_without_var_unscaled. + * when there're variations, get advance value from mtx_map in subset_plan*/ + unsigned get_new_gid_advance_unscaled (const hb_subset_plan_t *plan, + const hb_hashmap_t> *mtx_map, + unsigned new_gid, + const accelerator_t &_mtx) const + { + if (mtx_map->is_empty ()) + { + hb_codepoint_t old_gid = 0; + return plan->old_gid_for_new_gid (new_gid, &old_gid) ? + _mtx.get_advance_without_var_unscaled (old_gid) : 0; + } + return mtx_map->get (new_gid).first; + } + protected: UnsizedArrayOf longMetricZ; /* Paired advance width and leading @@ -346,12 +434,12 @@ struct hmtxvmtx DEFINE_SIZE_ARRAY (0, longMetricZ); }; -struct hmtx : hmtxvmtx { +struct hmtx : hmtxvmtx { static constexpr hb_tag_t tableTag = HB_OT_TAG_hmtx; static constexpr hb_tag_t variationsTag = HB_OT_TAG_HVAR; static constexpr bool is_horizontal = true; }; -struct vmtx : hmtxvmtx { +struct vmtx : hmtxvmtx { static constexpr hb_tag_t tableTag = HB_OT_TAG_vmtx; static constexpr hb_tag_t variationsTag = HB_OT_TAG_VVAR; static constexpr bool is_horizontal = false; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh index b9b5b76a97c..bcf12221619 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh @@ -49,7 +49,7 @@ struct BaseCoordFormat1 bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (likely (c->check_struct (this))); + return_trace (c->check_struct (this)); } protected: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh index ed8acdbd62a..73cf84ed39d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh @@ -35,69 +35,42 @@ #include "hb-set.hh" #include "hb-bimap.hh" +#include "OT/Layout/Common/Coverage.hh" +#include "OT/Layout/types.hh" -#ifndef HB_MAX_NESTING_LEVEL -#define HB_MAX_NESTING_LEVEL 64 -#endif -#ifndef HB_MAX_CONTEXT_LENGTH -#define HB_MAX_CONTEXT_LENGTH 64 -#endif -#ifndef HB_CLOSURE_MAX_STAGES -/* - * The maximum number of times a lookup can be applied during shaping. - * Used to limit the number of iterations of the closure algorithm. - * This must be larger than the number of times add_pause() is - * called in a collect_features call of any shaper. - */ -#define HB_CLOSURE_MAX_STAGES 32 -#endif - -#ifndef HB_MAX_SCRIPTS -#define HB_MAX_SCRIPTS 500 -#endif - -#ifndef HB_MAX_LANGSYS -#define HB_MAX_LANGSYS 2000 -#endif - -#ifndef HB_MAX_LANGSYS_FEATURE_COUNT -#define HB_MAX_LANGSYS_FEATURE_COUNT 50000 -#endif - -#ifndef HB_MAX_FEATURES -#define HB_MAX_FEATURES 750 -#endif - -#ifndef HB_MAX_FEATURE_INDICES -#define HB_MAX_FEATURE_INDICES 1500 -#endif - -#ifndef HB_MAX_LOOKUP_VISIT_COUNT -#define HB_MAX_LOOKUP_VISIT_COUNT 35000 -#endif +// TODO(garretrieger): cleanup these after migration. +using OT::Layout::Common::Coverage; +using OT::Layout::Common::RangeRecord; +using OT::Layout::SmallTypes; +using OT::Layout::MediumTypes; namespace OT { - -#define NOT_COVERED ((unsigned int) -1) - - template -static inline void Coverage_serialize (hb_serialize_context_t *c, +static inline bool ClassDef_serialize (hb_serialize_context_t *c, Iterator it); -template -static inline void ClassDef_serialize (hb_serialize_context_t *c, - Iterator it); - -static void ClassDef_remap_and_serialize ( +static bool ClassDef_remap_and_serialize ( hb_serialize_context_t *c, const hb_set_t &klasses, bool use_class_zero, hb_sorted_vector_t> &glyph_and_klass, /* IN/OUT */ hb_map_t *klass_map /*IN/OUT*/); +struct hb_collect_feature_substitutes_with_var_context_t +{ + const hb_map_t *axes_index_tag_map; + const hb_hashmap_t *axes_location; + hb_hashmap_t> *record_cond_idx_map; + hb_hashmap_t *feature_substitutes_map; + + // not stored in subset_plan + hb_set_t *feature_indices; + bool apply; + unsigned cur_record_idx; + hb_hashmap_t, unsigned> *conditionset_map; +}; struct hb_prune_langsys_context_t { @@ -164,24 +137,40 @@ struct hb_subset_layout_context_t : const hb_map_t *lookup_index_map; const hb_hashmap_t> *script_langsys_map; const hb_map_t *feature_index_map; + const hb_hashmap_t *feature_substitutes_map; + hb_hashmap_t> *feature_record_cond_idx_map; + unsigned cur_script_index; + unsigned cur_feature_var_record_idx; hb_subset_layout_context_t (hb_subset_context_t *c_, - hb_tag_t tag_, - hb_map_t *lookup_map_, - hb_hashmap_t> *script_langsys_map_, - hb_map_t *feature_index_map_) : + hb_tag_t tag_) : subset_context (c_), table_tag (tag_), - lookup_index_map (lookup_map_), - script_langsys_map (script_langsys_map_), - feature_index_map (feature_index_map_), cur_script_index (0xFFFFu), + cur_feature_var_record_idx (0u), script_count (0), langsys_count (0), feature_index_count (0), lookup_index_count (0) - {} + { + if (tag_ == HB_OT_TAG_GSUB) + { + lookup_index_map = &c_->plan->gsub_lookups; + script_langsys_map = &c_->plan->gsub_langsys; + feature_index_map = &c_->plan->gsub_features; + feature_substitutes_map = &c_->plan->gsub_feature_substitutes_map; + feature_record_cond_idx_map = c_->plan->user_axes_location.is_empty () ? nullptr : &c_->plan->gsub_feature_record_cond_idx_map; + } + else + { + lookup_index_map = &c_->plan->gpos_lookups; + script_langsys_map = &c_->plan->gpos_langsys; + feature_index_map = &c_->plan->gpos_features; + feature_substitutes_map = &c_->plan->gpos_feature_substitutes_map; + feature_record_cond_idx_map = c_->plan->user_axes_location.is_empty () ? nullptr : &c_->plan->gpos_feature_record_cond_idx_map; + } + } private: unsigned script_count; @@ -190,6 +179,7 @@ struct hb_subset_layout_context_t : unsigned lookup_index_count; }; +struct VariationStore; struct hb_collect_variation_indices_context_t : hb_dispatch_context_t { @@ -198,15 +188,27 @@ struct hb_collect_variation_indices_context_t : static return_t default_return_value () { return hb_empty_t (); } hb_set_t *layout_variation_indices; + hb_hashmap_t> *varidx_delta_map; + hb_font_t *font; + const VariationStore *var_store; const hb_set_t *glyph_set; const hb_map_t *gpos_lookups; + float *store_cache; hb_collect_variation_indices_context_t (hb_set_t *layout_variation_indices_, + hb_hashmap_t> *varidx_delta_map_, + hb_font_t *font_, + const VariationStore *var_store_, const hb_set_t *glyph_set_, - const hb_map_t *gpos_lookups_) : + const hb_map_t *gpos_lookups_, + float *store_cache_) : layout_variation_indices (layout_variation_indices_), + varidx_delta_map (varidx_delta_map_), + font (font_), + var_store (var_store_), glyph_set (glyph_set_), - gpos_lookups (gpos_lookups_) {} + gpos_lookups (gpos_lookups_), + store_cache (store_cache_) {} }; template @@ -315,6 +317,31 @@ struct subset_record_array_t const void *base; }; +template +struct subset_record_array_arg_t +{ + subset_record_array_arg_t (hb_subset_layout_context_t *c_, OutputArray* out_, + const void *base_, + Arg &&arg_) : subset_layout_context (c_), + out (out_), base (base_), arg (arg_) {} + + template + void + operator () (T&& record) + { + auto snap = subset_layout_context->subset_context->serializer->snapshot (); + bool ret = record.subset (subset_layout_context, base, arg); + if (!ret) subset_layout_context->subset_context->serializer->revert (snap); + else out->len++; + } + + private: + hb_subset_layout_context_t *subset_layout_context; + OutputArray *out; + const void *base; + Arg &&arg; +}; + /* * Helper to subset a RecordList/record array. Subsets each Record in the array and * discards the record if the subset operation returns false. @@ -326,6 +353,13 @@ struct operator () (hb_subset_layout_context_t *c, OutputArray* out, const void *base) const { return subset_record_array_t (c, out, base); } + + /* Variant with one extra argument passed to subset */ + template + subset_record_array_arg_t + operator () (hb_subset_layout_context_t *c, OutputArray* out, + const void *base, Arg &&arg) const + { return subset_record_array_arg_t (c, out, base, arg); } } HB_FUNCOBJ (subset_record_array); @@ -377,166 +411,6 @@ HB_FUNCOBJ (serialize_math_record_array); * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList */ -struct Record_sanitize_closure_t { - hb_tag_t tag; - const void *list_base; -}; - -template -struct Record -{ - int cmp (hb_tag_t a) const { return tag.cmp (a); } - - bool subset (hb_subset_layout_context_t *c, const void *base) const - { - TRACE_SUBSET (this); - auto *out = c->subset_context->serializer->embed (this); - if (unlikely (!out)) return_trace (false); - bool ret = out->offset.serialize_subset (c->subset_context, offset, base, c, &tag); - return_trace (ret); - } - - bool sanitize (hb_sanitize_context_t *c, const void *base) const - { - TRACE_SANITIZE (this); - const Record_sanitize_closure_t closure = {tag, base}; - return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure)); - } - - Tag tag; /* 4-byte Tag identifier */ - Offset16To - offset; /* Offset from beginning of object holding - * the Record */ - public: - DEFINE_SIZE_STATIC (6); -}; - -template -struct RecordArrayOf : SortedArray16Of> -{ - const Offset16To& get_offset (unsigned int i) const - { return (*this)[i].offset; } - Offset16To& get_offset (unsigned int i) - { return (*this)[i].offset; } - const Tag& get_tag (unsigned int i) const - { return (*this)[i].tag; } - unsigned int get_tags (unsigned int start_offset, - unsigned int *record_count /* IN/OUT */, - hb_tag_t *record_tags /* OUT */) const - { - if (record_count) - { - + this->sub_array (start_offset, record_count) - | hb_map (&Record::tag) - | hb_sink (hb_array (record_tags, *record_count)) - ; - } - return this->len; - } - bool find_index (hb_tag_t tag, unsigned int *index) const - { - return this->bfind (tag, index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); - } -}; - -template -struct RecordListOf : RecordArrayOf -{ - const Type& operator [] (unsigned int i) const - { return this+this->get_offset (i); } - - bool subset (hb_subset_context_t *c, - hb_subset_layout_context_t *l) const - { - TRACE_SUBSET (this); - auto *out = c->serializer->start_embed (*this); - if (unlikely (!c->serializer->extend_min (out))) return_trace (false); - - + this->iter () - | hb_apply (subset_record_array (l, out, this)) - ; - return_trace (true); - } - - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (RecordArrayOf::sanitize (c, this)); - } -}; - -struct Feature; - -struct RecordListOfFeature : RecordListOf -{ - bool subset (hb_subset_context_t *c, - hb_subset_layout_context_t *l) const - { - TRACE_SUBSET (this); - auto *out = c->serializer->start_embed (*this); - if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); - - unsigned count = this->len; - + hb_zip (*this, hb_range (count)) - | hb_filter (l->feature_index_map, hb_second) - | hb_map (hb_first) - | hb_apply (subset_record_array (l, out, this)) - ; - return_trace (true); - } -}; - -struct Script; -struct RecordListOfScript : RecordListOf