diff --git a/bin/update_copyright_year.sh b/bin/update_copyright_year.sh index fa7989d234b..fcdac6b935f 100644 --- a/bin/update_copyright_year.sh +++ b/bin/update_copyright_year.sh @@ -1,7 +1,7 @@ #!/bin/bash -f # -# Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 2026, 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 @@ -23,9 +23,13 @@ # questions. # -# Script to update the Copyright YEAR range in Mercurial & Git sources. +# Script to update the Copyright YEAR range in Git sources. # (Originally from xdono, Thanks!) +# To update Copyright years for changes in a specific branch, +# you use a command along these lines: +# $ git diff upstream/master... | lsdiff | cut -d '/' -f 2- | bash bin/update_copyright_year.sh -m - + #------------------------------------------------------------ copyright="Copyright" copyright_symbol="(c)" @@ -47,7 +51,7 @@ rm -f -r ${tmp} mkdir -p ${tmp} total=0 -usage="Usage: `basename "$0"` [-c company] [-y year] [-h|f]" +usage="Usage: `basename "$0"` [-c company] [-y year] [-m file] [-h|f]" Help() { # Display Help @@ -65,15 +69,18 @@ Help() echo "-b Specifies the base reference for change set lookup." echo "-f Updates the copyright for all change sets in a given year," echo " as specified by -y. Overrides -b flag." + echo "-m Read the list of modified files from the given file," + echo " use - to read from stdin" echo "-h Print this help." echo } full_year=false base_reference=master +modified_files_origin=""; # Process options -while getopts "b:c:fhy:" option; do +while getopts "b:c:fhm:y:" option; do case $option in b) # supplied base reference base_reference=${OPTARG} @@ -91,6 +98,9 @@ while getopts "b:c:fhy:" option; do y) # supplied company year year=${OPTARG} ;; + m) # modified files will be read from the given origin + modified_files_origin="${OPTARG}" + ;; \?) # illegal option echo "$usage" exit 1 @@ -110,18 +120,10 @@ git status &> /dev/null && git_found=true if [ "$git_found" != "true" ]; then echo "Error: Please execute script from within a JDK git repository." exit 1 -else - echo "Using Git version control system" - vcs_status=(git ls-files -m) - if [ "$full_year" = "true" ]; then - vcs_list_changesets=(git log --no-merges --since="${year}-01-01T00:00:00Z" --until="${year}-12-31T23:59:59Z" --pretty=tformat:"%H") - else - vcs_list_changesets=(git log --no-merges "${base_reference}..HEAD" --since="${year}-01-01T00:00:00Z" --until="${year}-12-31T23:59:59Z" --pretty=tformat:"%H") - fi - vcs_changeset_message=(git log -1 --pretty=tformat:"%B") # followed by ${changeset} - vcs_changeset_files=(git diff-tree --no-commit-id --name-only -r) # followed by ${changeset} fi +echo "Using Git version control system" + # Return true if it makes sense to edit this file saneFileToCheck() { @@ -168,6 +170,25 @@ updateFile() # file echo "${changed}" } +# Update the copyright year on files sent in stdin +updateFiles() # stdin: list of files to update +{ + count=0 + fcount=0 + while read i; do + fcount=`expr ${fcount} '+' 1` + if [ `updateFile "${i}"` = "true" ] ; then + count=`expr ${count} '+' 1` + fi + done + if [ ${count} -gt 0 ] ; then + printf " UPDATED year on %d of %d files.\n" ${count} ${fcount} + total=`expr ${total} '+' ${count}` + else + printf " None of the %d files were changed.\n" ${fcount} + fi +} + # Update the copyright year on all files changed by this changeset updateChangesetFiles() # changeset { @@ -178,18 +199,7 @@ updateChangesetFiles() # changeset | ${awk} -F' ' '{for(i=1;i<=NF;i++)print $i}' \ > ${files} if [ -f "${files}" -a -s "${files}" ] ; then - fcount=`cat ${files}| wc -l` - for i in `cat ${files}` ; do - if [ `updateFile "${i}"` = "true" ] ; then - count=`expr ${count} '+' 1` - fi - done - if [ ${count} -gt 0 ] ; then - printf " UPDATED year on %d of %d files.\n" ${count} ${fcount} - total=`expr ${total} '+' ${count}` - else - printf " None of the %d files were changed.\n" ${fcount} - fi + cat ${files} | updateFiles else printf " ERROR: No files changed in the changeset? Must be a mistake.\n" set -x @@ -204,67 +214,80 @@ updateChangesetFiles() # changeset } # Check if repository is clean +vcs_status=(git ls-files -m) previous=`"${vcs_status[@]}"|wc -l` if [ ${previous} -ne 0 ] ; then echo "WARNING: This repository contains previously edited working set files." echo " ${vcs_status[*]} | wc -l = `"${vcs_status[@]}" | wc -l`" fi -# Get all changesets this year -all_changesets=${tmp}/all_changesets -rm -f ${all_changesets} -"${vcs_list_changesets[@]}" > ${all_changesets} - -# Check changeset to see if it is Copyright only changes, filter changesets -if [ -s ${all_changesets} ] ; then - echo "Changesets made in ${year}: `cat ${all_changesets} | wc -l`" - index=0 - cat ${all_changesets} | while read changeset ; do - index=`expr ${index} '+' 1` - desc=${tmp}/desc.${changeset} - rm -f ${desc} - echo "------------------------------------------------" - "${vcs_changeset_message[@]}" "${changeset}" > ${desc} - printf "%d: %s\n%s\n" ${index} "${changeset}" "`cat ${desc}|head -1`" - if [ "${year}" = "2010" ] ; then - if cat ${desc} | grep -i -F "Added tag" > /dev/null ; then - printf " EXCLUDED tag changeset.\n" - elif cat ${desc} | grep -i -F rebrand > /dev/null ; then - printf " EXCLUDED rebrand changeset.\n" - elif cat ${desc} | grep -i -F copyright > /dev/null ; then - printf " EXCLUDED copyright changeset.\n" - else - updateChangesetFiles ${changeset} - fi - else - if cat ${desc} | grep -i -F "Added tag" > /dev/null ; then - printf " EXCLUDED tag changeset.\n" - elif cat ${desc} | grep -i -F "copyright year" > /dev/null ; then - printf " EXCLUDED copyright year changeset.\n" - else - updateChangesetFiles ${changeset} - fi - fi - rm -f ${desc} - done -fi - -if [ ${total} -gt 0 ] ; then - echo "---------------------------------------------" - echo "Updated the copyright year on a total of ${total} files." - if [ ${previous} -eq 0 ] ; then - echo "This count should match the count of modified files in the repository: ${vcs_status[*]}" - else - echo "WARNING: This repository contained previously edited working set files." - fi - echo " ${vcs_status[*]} | wc -l = `"${vcs_status[@]}" | wc -l`" +if [ "x$modified_files_origin" != "x" ]; then + cat $modified_files_origin | updateFiles else - echo "---------------------------------------------" - echo "No files were changed" - if [ ${previous} -ne 0 ] ; then - echo "WARNING: This repository contained previously edited working set files." - fi - echo " ${vcs_status[*]} | wc -l = `"${vcs_status[@]}" | wc -l`" + # Get all changesets this year + if [ "$full_year" = "true" ]; then + vcs_list_changesets=(git log --no-merges --since="${year}-01-01T00:00:00Z" --until="${year}-12-31T23:59:59Z" --pretty=tformat:"%H") + else + vcs_list_changesets=(git log --no-merges "${base_reference}..HEAD" --since="${year}-01-01T00:00:00Z" --until="${year}-12-31T23:59:59Z" --pretty=tformat:"%H") + fi + vcs_changeset_message=(git log -1 --pretty=tformat:"%B") # followed by ${changeset} + vcs_changeset_files=(git diff-tree --no-commit-id --name-only -r) # followed by ${changeset} + + all_changesets=${tmp}/all_changesets + rm -f ${all_changesets} + "${vcs_list_changesets[@]}" > ${all_changesets} + + # Check changeset to see if it is Copyright only changes, filter changesets + if [ -s ${all_changesets} ] ; then + echo "Changesets made in ${year}: `cat ${all_changesets} | wc -l`" + index=0 + cat ${all_changesets} | while read changeset ; do + index=`expr ${index} '+' 1` + desc=${tmp}/desc.${changeset} + rm -f ${desc} + echo "------------------------------------------------" + "${vcs_changeset_message[@]}" "${changeset}" > ${desc} + printf "%d: %s\n%s\n" ${index} "${changeset}" "`cat ${desc}|head -1`" + if [ "${year}" = "2010" ] ; then + if cat ${desc} | grep -i -F "Added tag" > /dev/null ; then + printf " EXCLUDED tag changeset.\n" + elif cat ${desc} | grep -i -F rebrand > /dev/null ; then + printf " EXCLUDED rebrand changeset.\n" + elif cat ${desc} | grep -i -F copyright > /dev/null ; then + printf " EXCLUDED copyright changeset.\n" + else + updateChangesetFiles ${changeset} + fi + else + if cat ${desc} | grep -i -F "Added tag" > /dev/null ; then + printf " EXCLUDED tag changeset.\n" + elif cat ${desc} | grep -i -F "copyright year" > /dev/null ; then + printf " EXCLUDED copyright year changeset.\n" + else + updateChangesetFiles ${changeset} + fi + fi + rm -f ${desc} + done + fi + + if [ ${total} -gt 0 ] ; then + echo "---------------------------------------------" + echo "Updated the copyright year on a total of ${total} files." + if [ ${previous} -eq 0 ] ; then + echo "This count should match the count of modified files in the repository: ${vcs_status[*]}" + else + echo "WARNING: This repository contained previously edited working set files." + fi + echo " ${vcs_status[*]} | wc -l = `"${vcs_status[@]}" | wc -l`" + else + echo "---------------------------------------------" + echo "No files were changed" + if [ ${previous} -ne 0 ] ; then + echo "WARNING: This repository contained previously edited working set files." + fi + echo " ${vcs_status[*]} | wc -l = `"${vcs_status[@]}" | wc -l`" + fi fi # Cleanup diff --git a/doc/testing.html b/doc/testing.html index 31f4fbd1778..195153c8612 100644 --- a/doc/testing.html +++ b/doc/testing.html @@ -72,6 +72,7 @@ id="toc-notes-for-specific-tests">Notes for Specific Tests
  • Non-US locale
  • PKCS11 Tests
  • +
  • SCTP Tests
  • Testing Ahead-of-time Optimizations
  • @@ -621,6 +622,21 @@ element of the appropriate @Artifact class. (See JTREG="JAVA_OPTIONS=-Djdk.test.lib.artifacts.nsslib-linux_aarch64=/path/to/NSS-libs"

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

    +

    SCTP Tests

    +

    The SCTP tests require the SCTP runtime library, which is often not +installed by default in popular Linux distributions. Without this +library, the SCTP tests will be skipped. If you want to enable the SCTP +tests, you should install the SCTP library before running the tests.

    +

    For distributions using the .deb packaging format and the apt tool +(such as Debian, Ubuntu, etc.), try this:

    +
    sudo apt install libsctp1
    +sudo modprobe sctp
    +lsmod | grep sctp
    +

    For distributions using the .rpm packaging format and the dnf tool +(such as Fedora, Red Hat, etc.), try this:

    +
    sudo dnf install -y lksctp-tools
    +sudo modprobe sctp
    +lsmod | grep sctp

    Testing Ahead-of-time Optimizations

    One way to improve test coverage of ahead-of-time (AOT) optimizations diff --git a/doc/testing.md b/doc/testing.md index b95f59de9fd..d0e54aab02b 100644 --- a/doc/testing.md +++ b/doc/testing.md @@ -640,6 +640,32 @@ $ make test TEST="jtreg:sun/security/pkcs11/Secmod/AddTrustedCert.java" \ For more notes about the PKCS11 tests, please refer to test/jdk/sun/security/pkcs11/README. + +### SCTP Tests + +The SCTP tests require the SCTP runtime library, which is often not installed +by default in popular Linux distributions. Without this library, the SCTP tests +will be skipped. If you want to enable the SCTP tests, you should install the +SCTP library before running the tests. + +For distributions using the .deb packaging format and the apt tool +(such as Debian, Ubuntu, etc.), try this: + +``` +sudo apt install libsctp1 +sudo modprobe sctp +lsmod | grep sctp +``` + +For distributions using the .rpm packaging format and the dnf tool +(such as Fedora, Red Hat, etc.), try this: + +``` +sudo dnf install -y lksctp-tools +sudo modprobe sctp +lsmod | grep sctp +``` + ### Testing Ahead-of-time Optimizations One way to improve test coverage of ahead-of-time (AOT) optimizations in diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 946b1332edc..02ea632e3ec 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2026, 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 @@ -972,6 +972,10 @@ define SetupRunJtregTestBody JTREG_AUTO_PROBLEM_LISTS += ProblemList-enable-preview.txt endif + ifneq ($$(findstring -XX:+UseCompactObjectHeaders, $$(JTREG_ALL_OPTIONS)), ) + JTREG_AUTO_PROBLEM_LISTS += ProblemList-coh.txt + endif + ifneq ($$(JTREG_EXTRA_PROBLEM_LISTS), ) # Accept both absolute paths as well as relative to the current test root. diff --git a/make/jdk/src/classes/build/tools/taglet/JSpec.java b/make/jdk/src/classes/build/tools/taglet/JSpec.java index 1c301e94a8a..7e1e0ca215e 100644 --- a/make/jdk/src/classes/build/tools/taglet/JSpec.java +++ b/make/jdk/src/classes/build/tools/taglet/JSpec.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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,13 +49,15 @@ import static com.sun.source.doctree.DocTree.Kind.*; * The tags can be used as follows: * *

    - * @jls section-number description
    + * @jls chapter.section description
    + * @jls preview-feature-chapter.section description
      * 
    * * For example: * *
      * @jls 3.4 Line Terminators
    + * @jls primitive-types-in-patterns-instanceof-switch-5.7.1 Exact Testing Conversions
      * 
    * * will produce the following HTML, depending on the file containing @@ -64,10 +66,24 @@ import static com.sun.source.doctree.DocTree.Kind.*; *
    {@code
      * 
    See Java Language Specification: *
    3.4 Line terminators + *
    + * 5.7.1 Exact Testing Conversions + * PREVIEW * }
    * - * Copies of JLS and JVMS are expected to have been placed in the {@code specs} - * folder. These documents are not included in open-source repositories. + * In inline tags (note you need manual JLS/JVMS prefix): + *
    + * JLS {@jls 3.4}
    + * 
    + * + * produces (note the section sign and no trailing dot): + *
    + * JLS §3.4
    + * 
    + * + * Copies of JLS, JVMS, and preview JLS and JVMS changes are expected to have + * been placed in the {@code specs} folder. These documents are not included + * in open-source repositories. */ public class JSpec implements Taglet { @@ -87,9 +103,9 @@ public class JSpec implements Taglet { } } - private String tagName; - private String specTitle; - private String idPrefix; + private final String tagName; + private final String specTitle; + private final String idPrefix; JSpec(String tagName, String specTitle, String idPrefix) { this.tagName = tagName; @@ -98,7 +114,7 @@ public class JSpec implements Taglet { } // Note: Matches special cases like @jvms 6.5.checkcast - private static final Pattern TAG_PATTERN = Pattern.compile("(?s)(.+ )?(?[1-9][0-9]*)(?
    [0-9a-z_.]*)( .*)?$"); + private static final Pattern TAG_PATTERN = Pattern.compile("(?s)(.+ )?(?([a-z0-9]+-)+)?(?[1-9][0-9]*)(?
    [0-9a-z_.]*)( .*)?$"); /** * Returns the set of locations in which the tag may be used. @@ -157,19 +173,50 @@ public class JSpec implements Taglet { .trim(); Matcher m = TAG_PATTERN.matcher(tagText); if (m.find()) { + // preview-feature-4.6 is preview-feature-, 4, .6 + String preview = m.group("preview"); // null if no preview feature String chapter = m.group("chapter"); String section = m.group("section"); String rootParent = currentPath().replaceAll("[^/]+", ".."); - String url = String.format("%1$s/specs/%2$s/%2$s-%3$s.html#%2$s-%3$s%4$s", - rootParent, idPrefix, chapter, section); + String url = preview == null ? + String.format("%1$s/specs/%2$s/%2$s-%3$s.html#%2$s-%3$s%4$s", + rootParent, idPrefix, chapter, section) : + String.format("%1$s/specs/%5$s%2$s.html#%2$s-%3$s%4$s", + rootParent, idPrefix, chapter, section, preview); + + var literal = expand(contents).trim(); + var prefix = (preview == null ? "" : preview) + chapter + section; + if (literal.startsWith(prefix)) { + var hasFullTitle = literal.length() > prefix.length(); + if (hasFullTitle) { + // Drop the preview identifier + literal = chapter + section + literal.substring(prefix.length()); + } else { + // No section sign if the tag refers to a chapter, like {@jvms 4} + String sectionSign = section.isEmpty() ? "" : "§"; + // Change whole text to "§chapter.x" in inline tags. + literal = sectionSign + chapter + section; + } + } sb.append("") - .append(expand(contents)) + .append(literal) .append(""); + if (preview != null) { + // Add PREVIEW superscript that links to JLS/JVMS 1.5.1 + // "Restrictions on the Use of Preview Features" + // Similar to how APIs link to the Preview info box warning + var sectionLink = String.format("%1$s/specs/%2$s/%2$s-%3$s.html#%2$s-%3$s%4$s", + rootParent, idPrefix, "1", ".5.1"); + sb.append("PREVIEW"); + } + if (tag.getKind() == DocTree.Kind.UNKNOWN_BLOCK_TAG) { sb.append("
    "); } diff --git a/src/demo/share/jfc/TableExample/OldJTable.java b/src/demo/share/jfc/TableExample/OldJTable.java deleted file mode 100644 index 8c77978fe8a..00000000000 --- a/src/demo/share/jfc/TableExample/OldJTable.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of Oracle nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * This source code is provided to illustrate the usage of a given feature - * or technique and has been deliberately simplified. Additional steps - * required for a production-quality application, such as security checks, - * input validation and proper error handling, might not be present in - * this sample code. - */ - - - -import java.util.EventObject; -import java.util.List; -import javax.swing.JTable; -import javax.swing.table.DefaultTableModel; -import javax.swing.table.TableCellEditor; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; - - -/** - * The OldJTable is an unsupported class containing some methods that were - * deleted from the JTable between releases 0.6 and 0.7 - */ -@SuppressWarnings("serial") -public class OldJTable extends JTable -{ - /* - * A new convenience method returning the index of the column in the - * co-ordinate space of the view. - */ - public int getColumnIndex(Object identifier) { - return getColumnModel().getColumnIndex(identifier); - } - -// -// Methods deleted from the JTable because they only work with the -// DefaultTableModel. -// - - public TableColumn addColumn(Object columnIdentifier, int width) { - return addColumn(columnIdentifier, width, null, null, null); - } - - public TableColumn addColumn(Object columnIdentifier, List columnData) { - return addColumn(columnIdentifier, -1, null, null, columnData); - } - - // Override the new JTable implementation - it will not add a column to the - // DefaultTableModel. - public TableColumn addColumn(Object columnIdentifier, int width, - TableCellRenderer renderer, - TableCellEditor editor) { - return addColumn(columnIdentifier, width, renderer, editor, null); - } - - public TableColumn addColumn(Object columnIdentifier, int width, - TableCellRenderer renderer, - TableCellEditor editor, List columnData) { - checkDefaultTableModel(); - - // Set up the model side first - DefaultTableModel m = (DefaultTableModel)getModel(); - m.addColumn(columnIdentifier, columnData.toArray()); - - // The column will have been added to the end, so the index of the - // column in the model is the last element. - TableColumn newColumn = new TableColumn( - m.getColumnCount()-1, width, renderer, editor); - super.addColumn(newColumn); - return newColumn; - } - - // Not possilble to make this work the same way ... change it so that - // it does not delete columns from the model. - public void removeColumn(Object columnIdentifier) { - super.removeColumn(getColumn(columnIdentifier)); - } - - public void addRow(Object[] rowData) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).addRow(rowData); - } - - public void addRow(List rowData) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).addRow(rowData.toArray()); - } - - public void removeRow(int rowIndex) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).removeRow(rowIndex); - } - - public void moveRow(int startIndex, int endIndex, int toIndex) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).moveRow(startIndex, endIndex, toIndex); - } - - public void insertRow(int rowIndex, Object[] rowData) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).insertRow(rowIndex, rowData); - } - - public void insertRow(int rowIndex, List rowData) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).insertRow(rowIndex, rowData.toArray()); - } - - public void setNumRows(int newSize) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).setNumRows(newSize); - } - - public void setDataVector(Object[][] newData, List columnIds) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).setDataVector( - newData, columnIds.toArray()); - } - - public void setDataVector(Object[][] newData, Object[] columnIds) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).setDataVector(newData, columnIds); - } - - protected void checkDefaultTableModel() { - if(!(dataModel instanceof DefaultTableModel)) - throw new InternalError("In order to use this method, the data model must be an instance of DefaultTableModel."); - } - -// -// Methods removed from JTable in the move from identifiers to ints. -// - - public Object getValueAt(Object columnIdentifier, int rowIndex) { - return super.getValueAt(rowIndex, getColumnIndex(columnIdentifier)); - } - - public boolean isCellEditable(Object columnIdentifier, int rowIndex) { - return super.isCellEditable(rowIndex, getColumnIndex(columnIdentifier)); - } - - public void setValueAt(Object aValue, Object columnIdentifier, int rowIndex) { - super.setValueAt(aValue, rowIndex, getColumnIndex(columnIdentifier)); - } - - public boolean editColumnRow(Object identifier, int row) { - return super.editCellAt(row, getColumnIndex(identifier)); - } - - public void moveColumn(Object columnIdentifier, Object targetColumnIdentifier) { - moveColumn(getColumnIndex(columnIdentifier), - getColumnIndex(targetColumnIdentifier)); - } - - public boolean isColumnSelected(Object identifier) { - return isColumnSelected(getColumnIndex(identifier)); - } - - public TableColumn addColumn(int modelColumn, int width) { - return addColumn(modelColumn, width, null, null); - } - - public TableColumn addColumn(int modelColumn) { - return addColumn(modelColumn, 75, null, null); - } - - /** - * Creates a new column with modelColumn, width, - * renderer, and editor and adds it to the end of - * the JTable's array of columns. This method also retrieves the - * name of the column using the model's getColumnName(modelColumn) - * method, and sets the both the header value and the identifier - * for this TableColumn accordingly. - *

    - * The modelColumn is the index of the column in the model which - * will supply the data for this column in the table. This, like the - * columnIdentifier in previous releases, does not change as the - * columns are moved in the view. - *

    - * For the rest of the JTable API, and all of its associated classes, - * columns are referred to in the co-ordinate system of the view, the - * index of the column in the model is kept inside the TableColumn - * and is used only to retrieve the information from the appropraite - * column in the model. - *

    - * - * @param modelColumn The index of the column in the model - * @param width The new column's width. Or -1 to use - * the default width - * @param renderer The renderer used with the new column. - * Or null to use the default renderer. - * @param editor The editor used with the new column. - * Or null to use the default editor. - */ - public TableColumn addColumn(int modelColumn, int width, - TableCellRenderer renderer, - TableCellEditor editor) { - TableColumn newColumn = new TableColumn( - modelColumn, width, renderer, editor); - addColumn(newColumn); - return newColumn; - } - -// -// Methods that had their arguments switched. -// - -// These won't work with the new table package. - -/* - public Object getValueAt(int columnIndex, int rowIndex) { - return super.getValueAt(rowIndex, columnIndex); - } - - public boolean isCellEditable(int columnIndex, int rowIndex) { - return super.isCellEditable(rowIndex, columnIndex); - } - - public void setValueAt(Object aValue, int columnIndex, int rowIndex) { - super.setValueAt(aValue, rowIndex, columnIndex); - } -*/ - - public boolean editColumnRow(int columnIndex, int rowIndex) { - return super.editCellAt(rowIndex, columnIndex); - } - - public boolean editColumnRow(int columnIndex, int rowIndex, EventObject e){ - return super.editCellAt(rowIndex, columnIndex, e); - } - - -} // End Of Class OldJTable diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index b9252cc56ff..a9ca91d9309 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1229,7 +1229,7 @@ public: // predicate controlling addressing modes bool size_fits_all_mem_uses(AddPNode* addp, int shift); - // Convert BootTest condition to Assembler condition. + // Convert BoolTest condition to Assembler condition. // Replicate the logic of cmpOpOper::ccode() and cmpOpUOper::ccode(). Assembler::Condition to_assembler_cond(BoolTest::mask cond); %} @@ -2579,7 +2579,7 @@ bool size_fits_all_mem_uses(AddPNode* addp, int shift) { return true; } -// Convert BootTest condition to Assembler condition. +// Convert BoolTest condition to Assembler condition. // Replicate the logic of cmpOpOper::ccode() and cmpOpUOper::ccode(). Assembler::Condition to_assembler_cond(BoolTest::mask cond) { Assembler::Condition result; diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 37a6a130e0d..c0621cbd5c2 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -1218,43 +1218,11 @@ void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) { __ bind(*op->stub()->continuation()); } -void LIR_Assembler::type_profile_helper(Register mdo, - ciMethodData *md, ciProfileData *data, - Register recv, Label* update_done) { +void LIR_Assembler::type_profile_helper(Register mdo, ciMethodData *md, + ciProfileData *data, Register recv) { - // Given a profile data offset, generate an Address which points to - // the corresponding slot in mdo->data(). - // Clobbers rscratch2. - auto slot_at = [=](ByteSize offset) -> Address { - return __ form_address(rscratch2, mdo, - md->byte_offset_of_slot(data, offset), - LogBytesPerWord); - }; - - for (uint i = 0; i < ReceiverTypeData::row_limit(); i++) { - Label next_test; - // See if the receiver is receiver[n]. - __ ldr(rscratch1, slot_at(ReceiverTypeData::receiver_offset(i))); - __ cmp(recv, rscratch1); - __ br(Assembler::NE, next_test); - __ addptr(slot_at(ReceiverTypeData::receiver_count_offset(i)), - DataLayout::counter_increment); - __ b(*update_done); - __ bind(next_test); - } - - // Didn't find receiver; find next empty slot and fill it in - for (uint i = 0; i < ReceiverTypeData::row_limit(); i++) { - Label next_test; - Address recv_addr(slot_at(ReceiverTypeData::receiver_offset(i))); - __ ldr(rscratch1, recv_addr); - __ cbnz(rscratch1, next_test); - __ str(recv, recv_addr); - __ mov(rscratch1, DataLayout::counter_increment); - __ str(rscratch1, slot_at(ReceiverTypeData::receiver_count_offset(i))); - __ b(*update_done); - __ bind(next_test); - } + int mdp_offset = md->byte_offset_of_slot(data, in_ByteSize(0)); + __ profile_receiver_type(recv, mdo, mdp_offset); } void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, Label* failure, Label* obj_is_null) { @@ -1316,14 +1284,9 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L __ b(*obj_is_null); __ bind(not_null); - Label update_done; Register recv = k_RInfo; __ load_klass(recv, obj); - type_profile_helper(mdo, md, data, recv, &update_done); - Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset())); - __ addptr(counter_addr, DataLayout::counter_increment); - - __ bind(update_done); + type_profile_helper(mdo, md, data, recv); } else { __ cbz(obj, *obj_is_null); } @@ -1430,13 +1393,9 @@ void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) { __ b(done); __ bind(not_null); - Label update_done; Register recv = k_RInfo; __ load_klass(recv, value); - type_profile_helper(mdo, md, data, recv, &update_done); - Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset())); - __ addptr(counter_addr, DataLayout::counter_increment); - __ bind(update_done); + type_profile_helper(mdo, md, data, recv); } else { __ cbz(value, done); } @@ -2540,13 +2499,9 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { if (C1OptimizeVirtualCallProfiling && known_klass != nullptr) { // We know the type that will be seen at this call site; we can // statically update the MethodData* rather than needing to do - // dynamic tests on the receiver type - - // NOTE: we should probably put a lock around this search to - // avoid collisions by concurrent compilations + // dynamic tests on the receiver type. ciVirtualCallData* vc_data = (ciVirtualCallData*) data; - uint i; - for (i = 0; i < VirtualCallData::row_limit(); i++) { + for (uint i = 0; i < VirtualCallData::row_limit(); i++) { ciKlass* receiver = vc_data->receiver(i); if (known_klass->equals(receiver)) { Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))); @@ -2554,36 +2509,13 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { return; } } - - // Receiver type not found in profile data; select an empty slot - - // Note that this is less efficient than it should be because it - // always does a write to the receiver part of the - // VirtualCallData rather than just the first time - for (i = 0; i < VirtualCallData::row_limit(); i++) { - ciKlass* receiver = vc_data->receiver(i); - if (receiver == nullptr) { - __ mov_metadata(rscratch1, known_klass->constant_encoding()); - Address recv_addr = - __ form_address(rscratch2, mdo, - md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i)), - LogBytesPerWord); - __ str(rscratch1, recv_addr); - Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))); - __ addptr(data_addr, DataLayout::counter_increment); - return; - } - } + // Receiver type is not found in profile data. + // Fall back to runtime helper to handle the rest at runtime. + __ mov_metadata(recv, known_klass->constant_encoding()); } else { __ load_klass(recv, recv); - Label update_done; - type_profile_helper(mdo, md, data, recv, &update_done); - // Receiver did not match any saved receiver and there is no empty row for it. - // Increment total counter to indicate polymorphic case. - __ addptr(counter_addr, DataLayout::counter_increment); - - __ bind(update_done); } + type_profile_helper(mdo, md, data, recv); } else { // Static call __ addptr(counter_addr, DataLayout::counter_increment); diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp index 21916a5f7dd..5af06fc6a1c 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp @@ -50,9 +50,8 @@ friend class ArrayCopyStub; Address stack_slot_address(int index, uint shift, Register tmp, int adjust = 0); // Record the type of the receiver in ReceiverTypeData - void type_profile_helper(Register mdo, - ciMethodData *md, ciProfileData *data, - Register recv, Label* update_done); + void type_profile_helper(Register mdo, ciMethodData *md, + ciProfileData *data, Register recv); void add_debug_info_for_branch(address adr, CodeEmitInfo* info); void casw(Register addr, Register newval, Register cmpval); diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index 9a035d9f40e..ad7bac4e067 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -85,26 +85,16 @@ void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, Dec } } -void ShenandoahBarrierSetAssembler::shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp, - bool tosca_live, - bool expand_call) { - if (ShenandoahSATBBarrier) { - satb_write_barrier_pre(masm, obj, pre_val, thread, tmp, rscratch1, tosca_live, expand_call); - } -} +void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + bool tosca_live, + bool expand_call) { + assert(ShenandoahSATBBarrier, "Should be checked by caller"); -void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call) { // If expand_call is true then we expand the call_VM_leaf macro // directly to skip generating the check by // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp. @@ -358,20 +348,20 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) { __ enter(/*strip_ret_addr*/true); __ push_call_clobbered_registers(); - satb_write_barrier_pre(masm /* masm */, - noreg /* obj */, - dst /* pre_val */, - rthread /* thread */, - tmp1 /* tmp1 */, - tmp2 /* tmp2 */, - true /* tosca_live */, - true /* expand_call */); + satb_barrier(masm /* masm */, + noreg /* obj */, + dst /* pre_val */, + rthread /* thread */, + tmp1 /* tmp1 */, + tmp2 /* tmp2 */, + true /* tosca_live */, + true /* expand_call */); __ pop_call_clobbered_registers(); __ leave(); } } -void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj) { +void ShenandoahBarrierSetAssembler::card_barrier(MacroAssembler* masm, Register obj) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); __ lsr(obj, obj, CardTable::card_shift()); @@ -394,13 +384,13 @@ void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register o void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) { - bool on_oop = is_reference_type(type); - if (!on_oop) { + // 1: non-reference types require no barriers + if (!is_reference_type(type)) { BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); return; } - // flatten object address if needed + // Flatten object address right away for simplicity: likely needed by barriers if (dst.index() == noreg && dst.offset() == 0) { if (dst.base() != tmp3) { __ mov(tmp3, dst.base()); @@ -409,20 +399,26 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet __ lea(tmp3, dst); } - shenandoah_write_barrier_pre(masm, - tmp3 /* obj */, - tmp2 /* pre_val */, - rthread /* thread */, - tmp1 /* tmp */, - val != noreg /* tosca_live */, - false /* expand_call */); + bool storing_non_null = (val != noreg); + // 2: pre-barrier: SATB needs the previous value + if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) { + satb_barrier(masm, + tmp3 /* obj */, + tmp2 /* pre_val */, + rthread /* thread */, + tmp1 /* tmp */, + rscratch1 /* tmp2 */, + storing_non_null /* tosca_live */, + false /* expand_call */); + } + + // Store! BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg); - bool in_heap = (decorators & IN_HEAP) != 0; - bool needs_post_barrier = (val != noreg) && in_heap && ShenandoahCardBarrier; - if (needs_post_barrier) { - store_check(masm, tmp3); + // 3: post-barrier: card barrier needs store address + if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) { + card_barrier(masm, tmp3); } } diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp index c0e708e1292..362fcae1ccd 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp @@ -40,23 +40,16 @@ class StubCodeGenerator; class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { private: - void satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call); - void shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp, - bool tosca_live, - bool expand_call); + void satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + bool tosca_live, + bool expand_call); - void store_check(MacroAssembler* masm, Register obj); + void card_barrier(MacroAssembler* masm, Register obj); void resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp = noreg); void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp = noreg); diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index 957c2aee1c1..2b506b241e0 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -240,15 +240,14 @@ void InterpreterMacroAssembler::load_resolved_klass_at_offset( // Rsub_klass: subklass // // Kills: -// r2, r5 +// r2 void InterpreterMacroAssembler::gen_subtype_check(Register Rsub_klass, Label& ok_is_subtype) { assert(Rsub_klass != r0, "r0 holds superklass"); assert(Rsub_klass != r2, "r2 holds 2ndary super array length"); - assert(Rsub_klass != r5, "r5 holds 2ndary super array scan ptr"); // Profile the not-null value's klass. - profile_typecheck(r2, Rsub_klass, r5); // blows r2, reloads r5 + profile_typecheck(r2, Rsub_klass); // blows r2 // Do the check. check_klass_subtype(Rsub_klass, r0, r2, ok_is_subtype); // blows r2 @@ -991,7 +990,6 @@ void InterpreterMacroAssembler::profile_final_call(Register mdp) { void InterpreterMacroAssembler::profile_virtual_call(Register receiver, Register mdp, - Register reg2, bool receiver_can_be_null) { if (ProfileInterpreter) { Label profile_continue; @@ -1009,7 +1007,7 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver, } // Record the receiver type. - record_klass_in_profile(receiver, mdp, reg2); + profile_receiver_type(receiver, mdp, 0); bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. @@ -1018,131 +1016,6 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver, } } -// This routine creates a state machine for updating the multi-row -// type profile at a virtual call site (or other type-sensitive bytecode). -// The machine visits each row (of receiver/count) until the receiver type -// is found, or until it runs out of rows. At the same time, it remembers -// the location of the first empty row. (An empty row records null for its -// receiver, and can be allocated for a newly-observed receiver type.) -// Because there are two degrees of freedom in the state, a simple linear -// search will not work; it must be a decision tree. Hence this helper -// function is recursive, to generate the required tree structured code. -// It's the interpreter, so we are trading off code space for speed. -// See below for example code. -void InterpreterMacroAssembler::record_klass_in_profile_helper( - Register receiver, Register mdp, - Register reg2, int start_row, - Label& done) { - if (TypeProfileWidth == 0) { - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - } else { - record_item_in_profile_helper(receiver, mdp, reg2, 0, done, TypeProfileWidth, - &VirtualCallData::receiver_offset, &VirtualCallData::receiver_count_offset); - } -} - -void InterpreterMacroAssembler::record_item_in_profile_helper(Register item, Register mdp, - Register reg2, int start_row, Label& done, int total_rows, - OffsetFunction item_offset_fn, OffsetFunction item_count_offset_fn) { - int last_row = total_rows - 1; - assert(start_row <= last_row, "must be work left to do"); - // Test this row for both the item and for null. - // Take any of three different outcomes: - // 1. found item => increment count and goto done - // 2. found null => keep looking for case 1, maybe allocate this cell - // 3. found something else => keep looking for cases 1 and 2 - // Case 3 is handled by a recursive call. - for (int row = start_row; row <= last_row; row++) { - Label next_test; - bool test_for_null_also = (row == start_row); - - // See if the item is item[n]. - int item_offset = in_bytes(item_offset_fn(row)); - test_mdp_data_at(mdp, item_offset, item, - (test_for_null_also ? reg2 : noreg), - next_test); - // (Reg2 now contains the item from the CallData.) - - // The item is item[n]. Increment count[n]. - int count_offset = in_bytes(item_count_offset_fn(row)); - increment_mdp_data_at(mdp, count_offset); - b(done); - bind(next_test); - - if (test_for_null_also) { - Label found_null; - // Failed the equality check on item[n]... Test for null. - if (start_row == last_row) { - // The only thing left to do is handle the null case. - cbz(reg2, found_null); - // Item did not match any saved item and there is no empty row for it. - // Increment total counter to indicate polymorphic case. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - b(done); - bind(found_null); - break; - } - // Since null is rare, make it be the branch-taken case. - cbz(reg2, found_null); - - // Put all the "Case 3" tests here. - record_item_in_profile_helper(item, mdp, reg2, start_row + 1, done, total_rows, - item_offset_fn, item_count_offset_fn); - - // Found a null. Keep searching for a matching item, - // but remember that this is an empty (unused) slot. - bind(found_null); - } - } - - // In the fall-through case, we found no matching item, but we - // observed the item[start_row] is null. - - // Fill in the item field and increment the count. - int item_offset = in_bytes(item_offset_fn(start_row)); - set_mdp_data_at(mdp, item_offset, item); - int count_offset = in_bytes(item_count_offset_fn(start_row)); - mov(reg2, DataLayout::counter_increment); - set_mdp_data_at(mdp, count_offset, reg2); - if (start_row > 0) { - b(done); - } -} - -// Example state machine code for three profile rows: -// // main copy of decision tree, rooted at row[1] -// if (row[0].rec == rec) { row[0].incr(); goto done; } -// if (row[0].rec != nullptr) { -// // inner copy of decision tree, rooted at row[1] -// if (row[1].rec == rec) { row[1].incr(); goto done; } -// if (row[1].rec != nullptr) { -// // degenerate decision tree, rooted at row[2] -// if (row[2].rec == rec) { row[2].incr(); goto done; } -// if (row[2].rec != nullptr) { count.incr(); goto done; } // overflow -// row[2].init(rec); goto done; -// } else { -// // remember row[1] is empty -// if (row[2].rec == rec) { row[2].incr(); goto done; } -// row[1].init(rec); goto done; -// } -// } else { -// // remember row[0] is empty -// if (row[1].rec == rec) { row[1].incr(); goto done; } -// if (row[2].rec == rec) { row[2].incr(); goto done; } -// row[0].init(rec); goto done; -// } -// done: - -void InterpreterMacroAssembler::record_klass_in_profile(Register receiver, - Register mdp, Register reg2) { - assert(ProfileInterpreter, "must be profiling"); - Label done; - - record_klass_in_profile_helper(receiver, mdp, reg2, 0, done); - - bind (done); -} - void InterpreterMacroAssembler::profile_ret(Register return_bci, Register mdp) { if (ProfileInterpreter) { @@ -1200,7 +1073,7 @@ void InterpreterMacroAssembler::profile_null_seen(Register mdp) { } } -void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass, Register reg2) { +void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass) { if (ProfileInterpreter) { Label profile_continue; @@ -1213,7 +1086,7 @@ void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass, mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); // Record the object type. - record_klass_in_profile(klass, mdp, reg2); + profile_receiver_type(klass, mdp, 0); } update_mdp_by_constant(mdp, mdp_delta); diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp index 2b230a3b73e..74d4430000d 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp @@ -273,15 +273,6 @@ class InterpreterMacroAssembler: public MacroAssembler { Register test_value_out, Label& not_equal_continue); - void record_klass_in_profile(Register receiver, Register mdp, - Register reg2); - void record_klass_in_profile_helper(Register receiver, Register mdp, - Register reg2, int start_row, - Label& done); - void record_item_in_profile_helper(Register item, Register mdp, - Register reg2, int start_row, Label& done, int total_rows, - OffsetFunction item_offset_fn, OffsetFunction item_count_offset_fn); - void update_mdp_by_offset(Register mdp_in, int offset_of_offset); void update_mdp_by_offset(Register mdp_in, Register reg, int offset_of_disp); void update_mdp_by_constant(Register mdp_in, int constant); @@ -295,11 +286,10 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_call(Register mdp); void profile_final_call(Register mdp); void profile_virtual_call(Register receiver, Register mdp, - Register scratch2, bool receiver_can_be_null = false); void profile_ret(Register return_bci, Register mdp); void profile_null_seen(Register mdp); - void profile_typecheck(Register mdp, Register klass, Register scratch); + void profile_typecheck(Register mdp, Register klass); void profile_typecheck_failed(Register mdp); void profile_switch_default(Register mdp); void profile_switch_case(Register index_in_scratch, Register mdp, diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 27428a5c558..f8b5a6f825c 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -2118,6 +2118,161 @@ Address MacroAssembler::argument_address(RegisterOrConstant arg_slot, } } +// Handle the receiver type profile update given the "recv" klass. +// +// Normally updates the ReceiverData (RD) that starts at "mdp" + "mdp_offset". +// If there are no matching or claimable receiver entries in RD, updates +// the polymorphic counter. +// +// This code expected to run by either the interpreter or JIT-ed code, without +// extra synchronization. For safety, receiver cells are claimed atomically, which +// avoids grossly misrepresenting the profiles under concurrent updates. For speed, +// counter updates are not atomic. +// +void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_offset) { + assert_different_registers(recv, mdp, rscratch1, rscratch2); + + int base_receiver_offset = in_bytes(ReceiverTypeData::receiver_offset(0)); + int end_receiver_offset = in_bytes(ReceiverTypeData::receiver_offset(ReceiverTypeData::row_limit())); + int poly_count_offset = in_bytes(CounterData::count_offset()); + int receiver_step = in_bytes(ReceiverTypeData::receiver_offset(1)) - base_receiver_offset; + int receiver_to_count_step = in_bytes(ReceiverTypeData::receiver_count_offset(0)) - base_receiver_offset; + + // Adjust for MDP offsets. + base_receiver_offset += mdp_offset; + end_receiver_offset += mdp_offset; + poly_count_offset += mdp_offset; + +#ifdef ASSERT + // We are about to walk the MDO slots without asking for offsets. + // Check that our math hits all the right spots. + for (uint c = 0; c < ReceiverTypeData::row_limit(); c++) { + int real_recv_offset = mdp_offset + in_bytes(ReceiverTypeData::receiver_offset(c)); + int real_count_offset = mdp_offset + in_bytes(ReceiverTypeData::receiver_count_offset(c)); + int offset = base_receiver_offset + receiver_step*c; + int count_offset = offset + receiver_to_count_step; + assert(offset == real_recv_offset, "receiver slot math"); + assert(count_offset == real_count_offset, "receiver count math"); + } + int real_poly_count_offset = mdp_offset + in_bytes(CounterData::count_offset()); + assert(poly_count_offset == real_poly_count_offset, "poly counter math"); +#endif + + // Corner case: no profile table. Increment poly counter and exit. + if (ReceiverTypeData::row_limit() == 0) { + increment(Address(mdp, poly_count_offset), DataLayout::counter_increment); + return; + } + + Register offset = rscratch2; + + Label L_loop_search_receiver, L_loop_search_empty; + Label L_restart, L_found_recv, L_found_empty, L_polymorphic, L_count_update; + + // The code here recognizes three major cases: + // A. Fastest: receiver found in the table + // B. Fast: no receiver in the table, and the table is full + // C. Slow: no receiver in the table, free slots in the table + // + // The case A performance is most important, as perfectly-behaved code would end up + // there, especially with larger TypeProfileWidth. The case B performance is + // important as well, this is where bulk of code would land for normally megamorphic + // cases. The case C performance is not essential, its job is to deal with installation + // races, we optimize for code density instead. Case C needs to make sure that receiver + // rows are only claimed once. This makes sure we never overwrite a row for another + // receiver and never duplicate the receivers in the list, making profile type-accurate. + // + // It is very tempting to handle these cases in a single loop, and claim the first slot + // without checking the rest of the table. But, profiling code should tolerate free slots + // in the table, as class unloading can clear them. After such cleanup, the receiver + // we need might be _after_ the free slot. Therefore, we need to let at least full scan + // to complete, before trying to install new slots. Splitting the code in several tight + // loops also helpfully optimizes for cases A and B. + // + // This code is effectively: + // + // restart: + // // Fastest: receiver is already installed + // for (i = 0; i < receiver_count(); i++) { + // if (receiver(i) == recv) goto found_recv(i); + // } + // + // // Fast: no receiver, but profile is full + // for (i = 0; i < receiver_count(); i++) { + // if (receiver(i) == null) goto found_null(i); + // } + // goto polymorphic + // + // // Slow: try to install receiver + // found_null(i): + // CAS(&receiver(i), null, recv); + // goto restart + // + // polymorphic: + // count++; + // return + // + // found_recv(i): + // *receiver_count(i)++ + // + + bind(L_restart); + + // Fastest: receiver is already installed + mov(offset, base_receiver_offset); + bind(L_loop_search_receiver); + ldr(rscratch1, Address(mdp, offset)); + cmp(rscratch1, recv); + br(Assembler::EQ, L_found_recv); + add(offset, offset, receiver_step); + sub(rscratch1, offset, end_receiver_offset); + cbnz(rscratch1, L_loop_search_receiver); + + // Fast: no receiver, but profile is full + mov(offset, base_receiver_offset); + bind(L_loop_search_empty); + ldr(rscratch1, Address(mdp, offset)); + cbz(rscratch1, L_found_empty); + add(offset, offset, receiver_step); + sub(rscratch1, offset, end_receiver_offset); + cbnz(rscratch1, L_loop_search_empty); + b(L_polymorphic); + + // Slow: try to install receiver + bind(L_found_empty); + + // Atomically swing receiver slot: null -> recv. + // + // The update uses CAS, which clobbers rscratch1. Therefore, rscratch2 + // is used to hold the destination address. This is safe because the + // offset is no longer needed after the address is computed. + + lea(rscratch2, Address(mdp, offset)); + cmpxchg(/*addr*/ rscratch2, /*expected*/ zr, /*new*/ recv, Assembler::xword, + /*acquire*/ false, /*release*/ false, /*weak*/ true, noreg); + + // CAS success means the slot now has the receiver we want. CAS failure means + // something had claimed the slot concurrently: it can be the same receiver we want, + // or something else. Since this is a slow path, we can optimize for code density, + // and just restart the search from the beginning. + b(L_restart); + + // Counter updates: + + // Increment polymorphic counter instead of receiver slot. + bind(L_polymorphic); + mov(offset, poly_count_offset); + b(L_count_update); + + // Found a receiver, convert its slot offset to corresponding count offset. + bind(L_found_recv); + add(offset, offset, receiver_to_count_step); + + bind(L_count_update); + increment(Address(mdp, offset), DataLayout::counter_increment); +} + + void MacroAssembler::call_VM_leaf_base(address entry_point, int number_of_arguments, Label *retaddr) { @@ -5606,12 +5761,11 @@ void MacroAssembler::adrp(Register reg1, const Address &dest, uint64_t &byte_off } void MacroAssembler::load_byte_map_base(Register reg) { - CardTable::CardValue* byte_map_base = - ((CardTableBarrierSet*)(BarrierSet::barrier_set()))->card_table()->byte_map_base(); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); - // Strictly speaking the byte_map_base isn't an address at all, and it might + // Strictly speaking the card table base isn't an address at all, and it might // even be negative. It is thus materialised as a constant. - mov(reg, (uint64_t)byte_map_base); + mov(reg, (uint64_t)ctbs->card_table_base_const()); } void MacroAssembler::build_frame(int framesize) { diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 4baa07d7d49..7eb6cea0614 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -1122,6 +1122,8 @@ public: Address argument_address(RegisterOrConstant arg_slot, int extra_slot_offset = 0); + void profile_receiver_type(Register recv, Register mdp, int mdp_offset); + void verify_sve_vector_length(Register tmp = rscratch1); void reinitialize_ptrue() { if (UseSVE > 0) { diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 7e2f333ba40..db653bcf236 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -6081,14 +6081,18 @@ class StubGenerator: public StubCodeGenerator { // static int implKyber12To16( // byte[] condensed, int index, short[] parsed, int parsedLength) {} // - // (parsedLength or (parsedLength - 48) must be divisible by 64.) + // we assume that parsed and condensed are allocated such that for + // n = (parsedLength + 63) / 64 + // n blocks of 96 bytes of input can be processed, i.e. + // index + n * 96 <= condensed.length and + // n * 64 <= parsed.length // // condensed (byte[]) = c_rarg0 // condensedIndex = c_rarg1 - // parsed (short[112 or 256]) = c_rarg2 - // parsedLength (112 or 256) = c_rarg3 + // parsed (short[]) = c_rarg2 + // parsedLength = c_rarg3 address generate_kyber12To16() { - Label L_F00, L_loop, L_end; + Label L_F00, L_loop; __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyber12To16_id; @@ -6209,75 +6213,8 @@ class StubGenerator: public StubCodeGenerator { vs_st2_post(vs_front(vb), __ T8H, parsed); __ sub(parsedLength, parsedLength, 64); - __ cmp(parsedLength, (u1)64); - __ br(Assembler::GE, L_loop); - __ cbz(parsedLength, L_end); - - // if anything is left it should be a final 72 bytes of input - // i.e. a final 48 12-bit values. so we handle this by loading - // 48 bytes into all 16B lanes of front(vin) and only 24 - // bytes into the lower 8B lane of back(vin) - vs_ld3_post(vs_front(vin), __ T16B, condensed); - vs_ld3(vs_back(vin), __ T8B, condensed); - - // Expand vin[0] into va[0:1], and vin[1] into va[2:3] and va[4:5] - // n.b. target elements 2 and 3 of va duplicate elements 4 and - // 5 and target element 2 of vb duplicates element 4. - __ ushll(va[0], __ T8H, vin[0], __ T8B, 0); - __ ushll2(va[1], __ T8H, vin[0], __ T16B, 0); - __ ushll(va[2], __ T8H, vin[1], __ T8B, 0); - __ ushll2(va[3], __ T8H, vin[1], __ T16B, 0); - __ ushll(va[4], __ T8H, vin[1], __ T8B, 0); - __ ushll2(va[5], __ T8H, vin[1], __ T16B, 0); - - // This time expand just the lower 8 lanes - __ ushll(vb[0], __ T8H, vin[3], __ T8B, 0); - __ ushll(vb[2], __ T8H, vin[4], __ T8B, 0); - __ ushll(vb[4], __ T8H, vin[4], __ T8B, 0); - - // shift lo byte of copy 1 of the middle stripe into the high byte - __ shl(va[2], __ T8H, va[2], 8); - __ shl(va[3], __ T8H, va[3], 8); - __ shl(vb[2], __ T8H, vb[2], 8); - - // expand vin[2] into va[6:7] and lower 8 lanes of vin[5] into - // vb[6] pre-shifted by 4 to ensure top bits of the input 12-bit - // int are in bit positions [4..11]. - __ ushll(va[6], __ T8H, vin[2], __ T8B, 4); - __ ushll2(va[7], __ T8H, vin[2], __ T16B, 4); - __ ushll(vb[6], __ T8H, vin[5], __ T8B, 4); - - // mask hi 4 bits of each 1st 12-bit int in pair from copy1 and - // shift lo 4 bits of each 2nd 12-bit int in pair to bottom of - // copy2 - __ andr(va[2], __ T16B, va[2], v31); - __ andr(va[3], __ T16B, va[3], v31); - __ ushr(va[4], __ T8H, va[4], 4); - __ ushr(va[5], __ T8H, va[5], 4); - __ andr(vb[2], __ T16B, vb[2], v31); - __ ushr(vb[4], __ T8H, vb[4], 4); - - - - // sum hi 4 bits and lo 8 bits of each 1st 12-bit int in pair and - // hi 8 bits plus lo 4 bits of each 2nd 12-bit int in pair - - // n.b. ordering ensures: i) inputs are consumed before they are - // overwritten ii) order of 16-bit results across succsessive - // pairs of vectors in va and then lower half of vb reflects order - // of corresponding 12-bit inputs - __ addv(va[0], __ T8H, va[0], va[2]); - __ addv(va[2], __ T8H, va[1], va[3]); - __ addv(va[1], __ T8H, va[4], va[6]); - __ addv(va[3], __ T8H, va[5], va[7]); - __ addv(vb[0], __ T8H, vb[0], vb[2]); - __ addv(vb[1], __ T8H, vb[4], vb[6]); - - // store 48 results interleaved as shorts - vs_st2_post(vs_front(va), __ T8H, parsed); - vs_st2_post(vs_front(vs_front(vb)), __ T8H, parsed); - - __ BIND(L_end); + __ cmp(parsedLength, (u1)0); + __ br(Assembler::GT, L_loop); __ leave(); // required for proper stackwalking of RuntimeStub frame __ mov(r0, zr); // return 0 diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index 07b469650f0..5d4f7103a84 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -3370,7 +3370,7 @@ void TemplateTable::invokevirtual_helper(Register index, __ load_klass(r0, recv); // profile this call - __ profile_virtual_call(r0, rlocals, r3); + __ profile_virtual_call(r0, rlocals); // get target Method & entry point __ lookup_virtual_method(r0, index, method); @@ -3500,7 +3500,7 @@ void TemplateTable::invokeinterface(int byte_no) { /*return_method=*/false); // profile this call - __ profile_virtual_call(r3, r13, r19); + __ profile_virtual_call(r3, r13); // Get declaring interface class from method, and itable index diff --git a/src/hotspot/cpu/arm/gc/shared/cardTableBarrierSetAssembler_arm.cpp b/src/hotspot/cpu/arm/gc/shared/cardTableBarrierSetAssembler_arm.cpp index 2427d46cafa..5d63035ac69 100644 --- a/src/hotspot/cpu/arm/gc/shared/cardTableBarrierSetAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/gc/shared/cardTableBarrierSetAssembler_arm.cpp @@ -67,9 +67,7 @@ void CardTableBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet d void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, Register tmp) { BLOCK_COMMENT("CardTablePostBarrier"); - BarrierSet* bs = BarrierSet::barrier_set(); - CardTableBarrierSet* ctbs = barrier_set_cast(bs); - CardTable* ct = ctbs->card_table(); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); Label L_cardtable_loop, L_done; @@ -83,7 +81,7 @@ void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembl __ sub(count, count, addr); // nb of cards // warning: Rthread has not been preserved - __ mov_address(tmp, (address) ct->byte_map_base()); + __ mov_address(tmp, (address)ctbs->card_table_base_const()); __ add(addr,tmp, addr); Register zero = __ zero_register(tmp); @@ -122,8 +120,7 @@ void CardTableBarrierSetAssembler::store_check_part1(MacroAssembler* masm, Regis assert(bs->kind() == BarrierSet::CardTableBarrierSet, "Wrong barrier set kind"); - CardTableBarrierSet* ctbs = barrier_set_cast(bs); - CardTable* ct = ctbs->card_table(); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); // Load card table base address. @@ -140,7 +137,7 @@ void CardTableBarrierSetAssembler::store_check_part1(MacroAssembler* masm, Regis Possible cause is a cache miss (card table base address resides in a rarely accessed area of thread descriptor). */ - __ mov_address(card_table_base, (address)ct->byte_map_base()); + __ mov_address(card_table_base, (address)ctbs->card_table_base_const()); } // The 2nd part of the store check. @@ -170,8 +167,8 @@ void CardTableBarrierSetAssembler::store_check_part2(MacroAssembler* masm, Regis void CardTableBarrierSetAssembler::set_card(MacroAssembler* masm, Register card_table_base, Address card_table_addr, Register tmp) { CardTableBarrierSet* ctbs = barrier_set_cast(BarrierSet::barrier_set()); - CardTable* ct = ctbs->card_table(); - if ((((uintptr_t)ct->byte_map_base() & 0xff) == 0)) { + + if ((((uintptr_t)ctbs->card_table_base_const() & 0xff) == 0)) { // Card table is aligned so the lowest byte of the table address base is zero. // This works only if the code is not saved for later use, possibly // in a context where the base would no longer be aligned. diff --git a/src/hotspot/cpu/ppc/assembler_ppc.hpp b/src/hotspot/cpu/ppc/assembler_ppc.hpp index 15e38411482..23775a3a52e 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp @@ -568,6 +568,9 @@ class Assembler : public AbstractAssembler { XSCVDPHP_OPCODE= (60u << OPCODE_SHIFT | 347u << 2 | 17u << 16), // XX2-FORM XXPERM_OPCODE = (60u << OPCODE_SHIFT | 26u << 3), XXSEL_OPCODE = (60u << OPCODE_SHIFT | 3u << 4), + XSCMPEQDP_OPCODE=(60u << OPCODE_SHIFT | 3u << 3), + XSCMPGEDP_OPCODE=(60u << OPCODE_SHIFT | 19u << 3), + XSCMPGTDP_OPCODE=(60u << OPCODE_SHIFT | 11u << 3), XXSPLTIB_OPCODE= (60u << OPCODE_SHIFT | 360u << 1), XVDIVDP_OPCODE = (60u << OPCODE_SHIFT | 120u << 3), XVABSSP_OPCODE = (60u << OPCODE_SHIFT | 409u << 2), @@ -2424,6 +2427,9 @@ class Assembler : public AbstractAssembler { inline void xscvdphp( VectorSRegister d, VectorSRegister b); inline void xxland( VectorSRegister d, VectorSRegister a, VectorSRegister b); inline void xxsel( VectorSRegister d, VectorSRegister a, VectorSRegister b, VectorSRegister c); + inline void xscmpeqdp(VectorSRegister t, VectorSRegister a, VectorSRegister b); // Requires Power9 + inline void xscmpgedp(VectorSRegister t, VectorSRegister a, VectorSRegister b); // Requires Power9 + inline void xscmpgtdp(VectorSRegister t, VectorSRegister a, VectorSRegister b); // Requires Power9 inline void xxspltib( VectorSRegister d, int ui8); inline void xvdivsp( VectorSRegister d, VectorSRegister a, VectorSRegister b); inline void xvdivdp( VectorSRegister d, VectorSRegister a, VectorSRegister b); diff --git a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp index 7e49ec7455d..4cda782067e 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp @@ -923,6 +923,10 @@ inline void Assembler::xxmrghw( VectorSRegister d, VectorSRegister a, VectorSReg inline void Assembler::xxmrglw( VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XXMRGHW_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); } inline void Assembler::xxsel( VectorSRegister d, VectorSRegister a, VectorSRegister b, VectorSRegister c) { emit_int32( XXSEL_OPCODE | vsrt(d) | vsra(a) | vsrb(b) | vsrc(c)); } +inline void Assembler::xscmpeqdp(VectorSRegister t, VectorSRegister a, VectorSRegister b) { emit_int32( XSCMPEQDP_OPCODE | vsrt(t) | vsra(a) | vsrb(b) );} +inline void Assembler::xscmpgedp(VectorSRegister t, VectorSRegister a, VectorSRegister b) { emit_int32( XSCMPGEDP_OPCODE | vsrt(t) | vsra(a) | vsrb(b) );} +inline void Assembler::xscmpgtdp(VectorSRegister t, VectorSRegister a, VectorSRegister b) { emit_int32( XSCMPGTDP_OPCODE | vsrt(t) | vsra(a) | vsrb(b) );} + // VSX Extended Mnemonics inline void Assembler::xxspltd( VectorSRegister d, VectorSRegister a, int x) { xxpermdi(d, a, a, x ? 3 : 0); } inline void Assembler::xxmrghd( VectorSRegister d, VectorSRegister a, VectorSRegister b) { xxpermdi(d, a, b, 0); } diff --git a/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp index edf348fdc50..73b6b132895 100644 --- a/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp @@ -664,3 +664,37 @@ void C2_MacroAssembler::reduceI(int opcode, Register dst, Register iSrc, VectorR fn_scalar_op(opcode, dst, iSrc, R0); // dst <- op(iSrc, R0) } +// Works for single and double precision floats. +// dst = (op1 cmp(cc) op2) ? src1 : src2; +// Unordered semantics are the same as for CmpF3Node/CmpD3Node which implement the fcmpl/dcmpl bytecodes. +// Comparing unordered values has the same result as when src1 is less than src2. +// So dst = src1 for <, <=, != and dst = src2 for >, >=, ==. +void C2_MacroAssembler::cmovF(int cc, VectorSRegister dst, VectorSRegister op1, VectorSRegister op2, + VectorSRegister src1, VectorSRegister src2, VectorSRegister tmp) { + // See operand cmpOp() for details. + bool invert_cond = (cc & 8) == 0; // invert reflects bcondCRbiIs0 + auto cmp = (Assembler::Condition)(cc & 3); + + switch(cmp) { + case Assembler::Condition::equal: + // Use false_result if "unordered". + xscmpeqdp(tmp, op1, op2); + break; + case Assembler::Condition::greater: + // Use false_result if "unordered". + xscmpgtdp(tmp, op1, op2); + break; + case Assembler::Condition::less: + // Use true_result if "unordered". + xscmpgedp(tmp, op1, op2); + invert_cond = !invert_cond; + break; + default: + assert(false, "unsupported compare condition: %d", cc); + ShouldNotReachHere(); + } + + VectorSRegister true_result = invert_cond ? src2 : src1; + VectorSRegister false_result = invert_cond ? src1 : src2; + xxsel(dst, false_result, true_result, tmp); +} diff --git a/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp b/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp index 5a114294c1f..e0dffec8396 100644 --- a/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp @@ -74,5 +74,7 @@ void count_positives(Register src, Register cnt, Register result, Register tmp1, Register tmp2); void reduceI(int opcode, Register dst, Register iSrc, VectorRegister vSrc, VectorRegister vTmp1, VectorRegister vTmp2); + void cmovF(int cc, VectorSRegister dst, VectorSRegister op1, VectorSRegister op2, + VectorSRegister src1, VectorSRegister src2, VectorSRegister tmp); #endif // CPU_PPC_C2_MACROASSEMBLER_PPC_HPP diff --git a/src/hotspot/cpu/ppc/gc/shared/cardTableBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shared/cardTableBarrierSetAssembler_ppc.cpp index 7404f7e2e5c..297ce57a394 100644 --- a/src/hotspot/cpu/ppc/gc/shared/cardTableBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shared/cardTableBarrierSetAssembler_ppc.cpp @@ -103,8 +103,7 @@ void CardTableBarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Registe void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, Register preserve) { - CardTableBarrierSet* ctbs = barrier_set_cast(BarrierSet::barrier_set()); - CardTable* ct = ctbs->card_table(); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); assert_different_registers(addr, count, R0); Label Lskip_loop, Lstore_loop; @@ -117,7 +116,7 @@ void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembl __ srdi(addr, addr, CardTable::card_shift()); __ srdi(count, count, CardTable::card_shift()); __ subf(count, addr, count); - __ add_const_optimized(addr, addr, (address)ct->byte_map_base(), R0); + __ add_const_optimized(addr, addr, (address)ctbs->card_table_base_const(), R0); __ addi(count, count, 1); __ li(R0, 0); __ mtctr(count); @@ -140,8 +139,8 @@ void CardTableBarrierSetAssembler::card_table_write(MacroAssembler* masm, } void CardTableBarrierSetAssembler::card_write_barrier_post(MacroAssembler* masm, Register store_addr, Register tmp) { - CardTableBarrierSet* bs = barrier_set_cast(BarrierSet::barrier_set()); - card_table_write(masm, bs->card_table()->byte_map_base(), tmp, store_addr); + CardTableBarrierSet* bs = CardTableBarrierSet::barrier_set(); + card_table_write(masm, bs->card_table_base_const(), tmp, store_addr); } void CardTableBarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index 1f1bc7622ed..c3bb1811031 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -50,14 +50,14 @@ #define __ masm-> -void ShenandoahBarrierSetAssembler::satb_write_barrier(MacroAssembler *masm, - Register base, RegisterOrConstant ind_or_offs, - Register tmp1, Register tmp2, Register tmp3, - MacroAssembler::PreservationLevel preservation_level) { +void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler *masm, + Register base, RegisterOrConstant ind_or_offs, + Register tmp1, Register tmp2, Register tmp3, + MacroAssembler::PreservationLevel preservation_level) { if (ShenandoahSATBBarrier) { - __ block_comment("satb_write_barrier (shenandoahgc) {"); - satb_write_barrier_impl(masm, 0, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); - __ block_comment("} satb_write_barrier (shenandoahgc)"); + __ block_comment("satb_barrier (shenandoahgc) {"); + satb_barrier_impl(masm, 0, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); + __ block_comment("} satb_barrier (shenandoahgc)"); } } @@ -198,11 +198,12 @@ void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, Dec // In "load mode", this register acts as a temporary register and must // thus not be 'noreg'. In "preloaded mode", its content will be sustained. // tmp1/tmp2: Temporary registers, one of which must be non-volatile in "preloaded mode". -void ShenandoahBarrierSetAssembler::satb_write_barrier_impl(MacroAssembler *masm, DecoratorSet decorators, - Register base, RegisterOrConstant ind_or_offs, - Register pre_val, - Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level) { +void ShenandoahBarrierSetAssembler::satb_barrier_impl(MacroAssembler *masm, DecoratorSet decorators, + Register base, RegisterOrConstant ind_or_offs, + Register pre_val, + Register tmp1, Register tmp2, + MacroAssembler::PreservationLevel preservation_level) { + assert(ShenandoahSATBBarrier, "Should be checked by caller"); assert_different_registers(tmp1, tmp2, pre_val, noreg); Label skip_barrier; @@ -574,13 +575,13 @@ void ShenandoahBarrierSetAssembler::load_at( if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) { if (ShenandoahSATBBarrier) { __ block_comment("keep_alive_barrier (shenandoahgc) {"); - satb_write_barrier_impl(masm, 0, noreg, noreg, dst, tmp1, tmp2, preservation_level); + satb_barrier_impl(masm, 0, noreg, noreg, dst, tmp1, tmp2, preservation_level); __ block_comment("} keep_alive_barrier (shenandoahgc)"); } } } -void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register base, RegisterOrConstant ind_or_offs, Register tmp) { +void ShenandoahBarrierSetAssembler::card_barrier(MacroAssembler* masm, Register base, RegisterOrConstant ind_or_offs, Register tmp) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); assert_different_registers(base, tmp, R0); @@ -603,21 +604,33 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler *masm, DecoratorSet Register base, RegisterOrConstant ind_or_offs, Register val, Register tmp1, Register tmp2, Register tmp3, MacroAssembler::PreservationLevel preservation_level) { - if (is_reference_type(type)) { - if (ShenandoahSATBBarrier) { - satb_write_barrier(masm, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); - } + // 1: non-reference types require no barriers + if (!is_reference_type(type)) { + BarrierSetAssembler::store_at(masm, decorators, type, + base, ind_or_offs, + val, + tmp1, tmp2, tmp3, + preservation_level); + return; } + bool storing_non_null = (val != noreg); + + // 2: pre-barrier: SATB needs the previous value + if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) { + satb_barrier(masm, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); + } + + // Store! BarrierSetAssembler::store_at(masm, decorators, type, base, ind_or_offs, val, tmp1, tmp2, tmp3, preservation_level); - // No need for post barrier if storing null - if (ShenandoahCardBarrier && is_reference_type(type) && val != noreg) { - store_check(masm, base, ind_or_offs, tmp1); + // 3: post-barrier: card barrier needs store address + if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) { + card_barrier(masm, base, ind_or_offs, tmp1); } } @@ -771,9 +784,6 @@ void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler *masm, Register b void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, Register preserve) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); - - ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); - CardTable* ct = bs->card_table(); assert_different_registers(addr, count, R0); Label L_skip_loop, L_store_loop; diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp index b058dcf1a2e..52615a740af 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp @@ -45,15 +45,15 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { private: /* ==== Actual barrier implementations ==== */ - void satb_write_barrier_impl(MacroAssembler* masm, DecoratorSet decorators, - Register base, RegisterOrConstant ind_or_offs, - Register pre_val, - Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level); + void satb_barrier_impl(MacroAssembler* masm, DecoratorSet decorators, + Register base, RegisterOrConstant ind_or_offs, + Register pre_val, + Register tmp1, Register tmp2, + MacroAssembler::PreservationLevel preservation_level); - void store_check(MacroAssembler* masm, - Register base, RegisterOrConstant ind_or_offs, - Register tmp); + void card_barrier(MacroAssembler* masm, + Register base, RegisterOrConstant ind_or_offs, + Register tmp); void load_reference_barrier_impl(MacroAssembler* masm, DecoratorSet decorators, Register base, RegisterOrConstant ind_or_offs, @@ -85,10 +85,10 @@ public: #endif /* ==== Available barriers (facades of the actual implementations) ==== */ - void satb_write_barrier(MacroAssembler* masm, - Register base, RegisterOrConstant ind_or_offs, - Register tmp1, Register tmp2, Register tmp3, - MacroAssembler::PreservationLevel preservation_level); + void satb_barrier(MacroAssembler* masm, + Register base, RegisterOrConstant ind_or_offs, + Register tmp1, Register tmp2, Register tmp3, + MacroAssembler::PreservationLevel preservation_level); void load_reference_barrier(MacroAssembler* masm, DecoratorSet decorators, Register base, RegisterOrConstant ind_or_offs, diff --git a/src/hotspot/cpu/ppc/matcher_ppc.hpp b/src/hotspot/cpu/ppc/matcher_ppc.hpp index aad41fb7b1c..b50de6323de 100644 --- a/src/hotspot/cpu/ppc/matcher_ppc.hpp +++ b/src/hotspot/cpu/ppc/matcher_ppc.hpp @@ -64,12 +64,10 @@ return true; } - // Use conditional move (CMOVL) on Power7. static constexpr int long_cmove_cost() { return 0; } // this only makes long cmoves more expensive than int cmoves - // Suppress CMOVF. Conditional move available (sort of) on PPC64 only from P7 onwards. Not exploited yet. - // fsel doesn't accept a condition register as input, so this would be slightly different. - static int float_cmove_cost() { return ConditionalMoveLimit; } + // Suppress CMOVF for Power8 because there are no fast nodes. + static int float_cmove_cost() { return (PowerArchitecturePPC64 >= 9) ? 0 : ConditionalMoveLimit; } // This affects two different things: // - how Decode nodes are matched diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 2a0a9149bb3..d926fabd353 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -3024,7 +3024,6 @@ encode %{ %} enc_class postalloc_expand_encode_oop(iRegNdst dst, iRegPdst src, flagsReg crx) %{ - // use isel instruction with Power 7 cmpP_reg_imm16Node *n_compare = new cmpP_reg_imm16Node(); encodeP_subNode *n_sub_base = new encodeP_subNode(); encodeP_shiftNode *n_shift = new encodeP_shiftNode(); @@ -3099,7 +3098,6 @@ encode %{ n_shift->_opnds[1] = op_src; n_shift->_bottom_type = _bottom_type; - // use isel instruction with Power 7 decodeN_addNode *n_add_base = new decodeN_addNode(); n_add_base->add_req(n_region, n_shift); n_add_base->_opnds[0] = op_dst; @@ -6618,7 +6616,6 @@ instruct cond_sub_base(iRegNdst dst, flagsRegSrc crx, iRegPsrc src1) %{ ins_pipe(pipe_class_default); %} -// Power 7 can use isel instruction instruct cond_set_0_oop(iRegNdst dst, flagsRegSrc crx, iRegPsrc src1) %{ // The match rule is needed to make it a 'MachTypeNode'! match(Set dst (EncodeP (Binary crx src1))); @@ -7293,7 +7290,6 @@ instruct cmovF_reg(cmpOp cmp, flagsRegSrc crx, regF dst, regF src) %{ ins_variable_size_depending_on_alignment(true); format %{ "CMOVEF $cmp, $crx, $dst, $src\n\t" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode %{ Label done; @@ -7313,7 +7309,6 @@ instruct cmovD_reg(cmpOp cmp, flagsRegSrc crx, regD dst, regD src) %{ ins_variable_size_depending_on_alignment(true); format %{ "CMOVEF $cmp, $crx, $dst, $src\n\t" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode %{ Label done; @@ -7326,6 +7321,70 @@ instruct cmovD_reg(cmpOp cmp, flagsRegSrc crx, regD dst, regD src) %{ ins_pipe(pipe_class_default); %} +instruct cmovF_cmpF(cmpOp cop, regF op1, regF op2, regF dst, regF false_result, regF true_result, regD tmp) %{ + match(Set dst (CMoveF (Binary cop (CmpF op1 op2)) (Binary false_result true_result))); + predicate(PowerArchitecturePPC64 >= 9); + effect(TEMP tmp); + ins_cost(2*DEFAULT_COST); + format %{ "cmovF_cmpF $dst = ($op1 $cop $op2) ? $true_result : $false_result\n\t" %} + size(8); + ins_encode %{ + __ cmovF($cop$$cmpcode, $dst$$FloatRegister->to_vsr(), + $op1$$FloatRegister->to_vsr(), $op2$$FloatRegister->to_vsr(), + $true_result$$FloatRegister->to_vsr(), $false_result$$FloatRegister->to_vsr(), + $tmp$$FloatRegister->to_vsr()); + %} + ins_pipe(pipe_class_default); +%} + +instruct cmovF_cmpD(cmpOp cop, regD op1, regD op2, regF dst, regF false_result, regF true_result, regD tmp) %{ + match(Set dst (CMoveF (Binary cop (CmpD op1 op2)) (Binary false_result true_result))); + predicate(PowerArchitecturePPC64 >= 9); + effect(TEMP tmp); + ins_cost(2*DEFAULT_COST); + format %{ "cmovF_cmpD $dst = ($op1 $cop $op2) ? $true_result : $false_result\n\t" %} + size(8); + ins_encode %{ + __ cmovF($cop$$cmpcode, $dst$$FloatRegister->to_vsr(), + $op1$$FloatRegister->to_vsr(), $op2$$FloatRegister->to_vsr(), + $true_result$$FloatRegister->to_vsr(), $false_result$$FloatRegister->to_vsr(), + $tmp$$FloatRegister->to_vsr()); + %} + ins_pipe(pipe_class_default); +%} + +instruct cmovD_cmpD(cmpOp cop, regD op1, regD op2, regD dst, regD false_result, regD true_result, regD tmp) %{ + match(Set dst (CMoveD (Binary cop (CmpD op1 op2)) (Binary false_result true_result))); + predicate(PowerArchitecturePPC64 >= 9); + effect(TEMP tmp); + ins_cost(2*DEFAULT_COST); + format %{ "cmovD_cmpD $dst = ($op1 $cop $op2) ? $true_result : $false_result\n\t" %} + size(8); + ins_encode %{ + __ cmovF($cop$$cmpcode, $dst$$FloatRegister->to_vsr(), + $op1$$FloatRegister->to_vsr(), $op2$$FloatRegister->to_vsr(), + $true_result$$FloatRegister->to_vsr(), $false_result$$FloatRegister->to_vsr(), + $tmp$$FloatRegister->to_vsr()); + %} + ins_pipe(pipe_class_default); +%} + +instruct cmovD_cmpF(cmpOp cop, regF op1, regF op2, regD dst, regD false_result, regD true_result, regD tmp) %{ + match(Set dst (CMoveD (Binary cop (CmpF op1 op2)) (Binary false_result true_result))); + predicate(PowerArchitecturePPC64 >= 9); + effect(TEMP tmp); + ins_cost(2*DEFAULT_COST); + format %{ "cmovD_cmpF $dst = ($op1 $cop $op2) ? $true_result : $false_result\n\t" %} + size(8); + ins_encode %{ + __ cmovF($cop$$cmpcode, $dst$$FloatRegister->to_vsr(), + $op1$$FloatRegister->to_vsr(), $op2$$FloatRegister->to_vsr(), + $true_result$$FloatRegister->to_vsr(), $false_result$$FloatRegister->to_vsr(), + $tmp$$FloatRegister->to_vsr()); + %} + ins_pipe(pipe_class_default); +%} + //----------Compare-And-Swap--------------------------------------------------- // CompareAndSwap{P,I,L} have more than one output, therefore "CmpI @@ -8492,7 +8551,6 @@ instruct cmovI_bne_negI_reg(iRegIdst dst, flagsRegSrc crx, iRegIsrc src1) %{ ins_variable_size_depending_on_alignment(true); format %{ "CMOVE $dst, neg($src1), $crx" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode %{ Label done; @@ -8551,7 +8609,6 @@ instruct cmovL_bne_negL_reg(iRegLdst dst, flagsRegSrc crx, iRegLsrc src1) %{ ins_variable_size_depending_on_alignment(true); format %{ "CMOVE $dst, neg($src1), $crx" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode %{ Label done; @@ -10262,7 +10319,6 @@ instruct cmovI_bso_stackSlotL(iRegIdst dst, flagsRegSrc crx, stackSlotL src) %{ ins_variable_size_depending_on_alignment(true); format %{ "cmovI $crx, $dst, $src" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -10276,7 +10332,6 @@ instruct cmovI_bso_reg(iRegIdst dst, flagsRegSrc crx, regD src) %{ ins_variable_size_depending_on_alignment(true); format %{ "cmovI $crx, $dst, $src" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode( enc_cmove_bso_reg(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -10439,7 +10494,6 @@ instruct cmovL_bso_stackSlotL(iRegLdst dst, flagsRegSrc crx, stackSlotL src) %{ ins_variable_size_depending_on_alignment(true); format %{ "cmovL $crx, $dst, $src" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -10453,7 +10507,6 @@ instruct cmovL_bso_reg(iRegLdst dst, flagsRegSrc crx, regD src) %{ ins_variable_size_depending_on_alignment(true); format %{ "cmovL $crx, $dst, $src" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode( enc_cmove_bso_reg(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -11080,7 +11133,6 @@ instruct cmov_bns_less(flagsReg crx) %{ ins_variable_size_depending_on_alignment(true); format %{ "cmov $crx" %} - // Worst case is branch + move + stop, no stop without scheduler. size(12); ins_encode %{ Label done; diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index dd6c8556307..3cbbb783258 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -88,26 +88,16 @@ void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, Dec } } -void ShenandoahBarrierSetAssembler::shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp, - bool tosca_live, - bool expand_call) { - if (ShenandoahSATBBarrier) { - satb_write_barrier_pre(masm, obj, pre_val, thread, tmp, t0, tosca_live, expand_call); - } -} +void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + bool tosca_live, + bool expand_call) { + assert(ShenandoahSATBBarrier, "Should be checked by caller"); -void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call) { // If expand_call is true then we expand the call_VM_leaf macro // directly to skip generating the check by // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp. @@ -376,21 +366,21 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) { __ enter(); __ push_call_clobbered_registers(); - satb_write_barrier_pre(masm /* masm */, - noreg /* obj */, - dst /* pre_val */, - xthread /* thread */, - tmp1 /* tmp1 */, - tmp2 /* tmp2 */, - true /* tosca_live */, - true /* expand_call */); + satb_barrier(masm /* masm */, + noreg /* obj */, + dst /* pre_val */, + xthread /* thread */, + tmp1 /* tmp1 */, + tmp2 /* tmp2 */, + true /* tosca_live */, + true /* expand_call */); __ pop_call_clobbered_registers(); __ leave(); } } -void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj) { - assert(ShenandoahCardBarrier, "Did you mean to enable ShenandoahCardBarrier?"); +void ShenandoahBarrierSetAssembler::card_barrier(MacroAssembler* masm, Register obj) { + assert(ShenandoahCardBarrier, "Should have been checked by caller"); __ srli(obj, obj, CardTable::card_shift()); @@ -413,13 +403,13 @@ void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register o void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) { - bool on_oop = is_reference_type(type); - if (!on_oop) { + // 1: non-reference types require no barriers + if (!is_reference_type(type)) { BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); return; } - // flatten object address if needed + // Flatten object address right away for simplicity: likely needed by barriers if (dst.offset() == 0) { if (dst.base() != tmp3) { __ mv(tmp3, dst.base()); @@ -428,20 +418,26 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet __ la(tmp3, dst); } - shenandoah_write_barrier_pre(masm, - tmp3 /* obj */, - tmp2 /* pre_val */, - xthread /* thread */, - tmp1 /* tmp */, - val != noreg /* tosca_live */, - false /* expand_call */); + bool storing_non_null = (val != noreg); + // 2: pre-barrier: SATB needs the previous value + if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) { + satb_barrier(masm, + tmp3 /* obj */, + tmp2 /* pre_val */, + xthread /* thread */, + tmp1 /* tmp */, + t0 /* tmp2 */, + storing_non_null /* tosca_live */, + false /* expand_call */); + } + + // Store! BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg); - bool in_heap = (decorators & IN_HEAP) != 0; - bool needs_post_barrier = (val != noreg) && in_heap && ShenandoahCardBarrier; - if (needs_post_barrier) { - store_check(masm, tmp3); + // 3: post-barrier: card barrier needs store address + if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) { + card_barrier(masm, tmp3); } } diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp index c8a7c35fb83..5085be26b2e 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp @@ -41,23 +41,16 @@ class StubCodeGenerator; class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { private: - void satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call); - void shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp, - bool tosca_live, - bool expand_call); + void satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + bool tosca_live, + bool expand_call); - void store_check(MacroAssembler* masm, Register obj); + void card_barrier(MacroAssembler* masm, Register obj); void resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp = noreg); void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp = noreg); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 43b17a13c20..fb30f64e9ed 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -5110,9 +5110,8 @@ void MacroAssembler::get_thread(Register thread) { } void MacroAssembler::load_byte_map_base(Register reg) { - CardTable::CardValue* byte_map_base = - ((CardTableBarrierSet*)(BarrierSet::barrier_set()))->card_table()->byte_map_base(); - mv(reg, (uint64_t)byte_map_base); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); + mv(reg, (uint64_t)ctbs->card_table_base_const()); } void MacroAssembler::build_frame(int framesize) { diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp index 5a3644f70bb..0fb529d1683 100644 --- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp @@ -708,7 +708,6 @@ void TemplateTable::index_check(Register array, Register index) { __ mv(x11, index); } Label ok; - __ sext(index, index, 32); __ bltu(index, length, ok); __ mv(x13, array); __ mv(t1, Interpreter::_throw_ArrayIndexOutOfBoundsException_entry); @@ -1052,7 +1051,7 @@ void TemplateTable::aastore() { transition(vtos, vtos); // stack: ..., array, index, value __ ld(x10, at_tos()); // value - __ ld(x12, at_tos_p1()); // index + __ lw(x12, at_tos_p1()); // index __ ld(x13, at_tos_p2()); // array index_check(x13, x12); // kills x11 @@ -1462,9 +1461,9 @@ void TemplateTable::iinc() { transition(vtos, vtos); __ load_signed_byte(x11, at_bcp(2)); // get constant locals_index(x12); - __ ld(x10, iaddress(x12, x10, _masm)); + __ lw(x10, iaddress(x12, x10, _masm)); __ addw(x10, x10, x11); - __ sd(x10, iaddress(x12, t0, _masm)); + __ sw(x10, iaddress(x12, t0, _masm)); } void TemplateTable::wide_iinc() { @@ -1477,9 +1476,9 @@ void TemplateTable::wide_iinc() { __ orr(x11, x11, t1); locals_index_wide(x12); - __ ld(x10, iaddress(x12, t0, _masm)); + __ lw(x10, iaddress(x12, t0, _masm)); __ addw(x10, x10, x11); - __ sd(x10, iaddress(x12, t0, _masm)); + __ sw(x10, iaddress(x12, t0, _masm)); } void TemplateTable::convert() { diff --git a/src/hotspot/cpu/s390/gc/shared/cardTableBarrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/shared/cardTableBarrierSetAssembler_s390.cpp index a0da6ebe682..9bb7f63ff31 100644 --- a/src/hotspot/cpu/s390/gc/shared/cardTableBarrierSetAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/gc/shared/cardTableBarrierSetAssembler_s390.cpp @@ -83,8 +83,7 @@ void CardTableBarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Registe void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, bool do_return) { - CardTableBarrierSet* ctbs = barrier_set_cast(BarrierSet::barrier_set()); - CardTable* ct = ctbs->card_table(); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); NearLabel doXC, done; assert_different_registers(Z_R0, Z_R1, addr, count); @@ -105,7 +104,7 @@ void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembl __ add2reg_with_index(count, -BytesPerHeapOop, count, addr); // Get base address of card table. - __ load_const_optimized(Z_R1, (address)ct->byte_map_base()); + __ load_const_optimized(Z_R1, (address)ctbs->card_table_base_const()); // count = (count>>shift) - (addr>>shift) __ z_srlg(addr, addr, CardTable::card_shift()); @@ -179,13 +178,12 @@ void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembl void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register store_addr, Register tmp) { // Does a store check for the oop in register obj. The content of // register obj is destroyed afterwards. - CardTableBarrierSet* ctbs = barrier_set_cast(BarrierSet::barrier_set()); - CardTable* ct = ctbs->card_table(); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); assert_different_registers(store_addr, tmp); __ z_srlg(store_addr, store_addr, CardTable::card_shift()); - __ load_absolute_address(tmp, (address)ct->byte_map_base()); + __ load_absolute_address(tmp, (address)ctbs->card_table_base_const()); __ z_agr(store_addr, tmp); __ z_mvi(0, store_addr, CardTable::dirty_card_val()); } diff --git a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp index 2b91662ddb5..65e6b4e01fc 100644 --- a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp @@ -95,11 +95,7 @@ void CardTableBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet d void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, Register tmp) { - BarrierSet *bs = BarrierSet::barrier_set(); - CardTableBarrierSet* ctbs = barrier_set_cast(bs); - CardTable* ct = ctbs->card_table(); - intptr_t disp = (intptr_t) ct->byte_map_base(); - SHENANDOAHGC_ONLY(assert(!UseShenandoahGC, "Shenandoah byte_map_base is not constant.");) + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); Label L_loop, L_done; const Register end = count; @@ -115,7 +111,7 @@ void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembl __ shrptr(end, CardTable::card_shift()); __ subptr(end, addr); // end --> cards count - __ mov64(tmp, disp); + __ mov64(tmp, (intptr_t)ctbs->card_table_base_const()); __ addptr(addr, tmp); __ BIND(L_loop); __ movb(Address(addr, count, Address::times_1), 0); @@ -128,10 +124,7 @@ __ BIND(L_done); void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Address dst) { // Does a store check for the oop in register obj. The content of // register obj is destroyed afterwards. - BarrierSet* bs = BarrierSet::barrier_set(); - - CardTableBarrierSet* ctbs = barrier_set_cast(bs); - CardTable* ct = ctbs->card_table(); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); __ shrptr(obj, CardTable::card_shift()); @@ -142,7 +135,7 @@ void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register ob // So this essentially converts an address to a displacement and it will // never need to be relocated. On 64bit however the value may be too // large for a 32bit displacement. - intptr_t byte_map_base = (intptr_t)ct->byte_map_base(); + intptr_t byte_map_base = (intptr_t)ctbs->card_table_base_const(); if (__ is_simm32(byte_map_base)) { card_addr = Address(noreg, obj, Address::times_1, byte_map_base); } else { diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index 9e321391f6c..97829a10a3b 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -174,24 +174,14 @@ void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, Dec } } -void ShenandoahBarrierSetAssembler::shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register tmp, - bool tosca_live, - bool expand_call) { +void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register tmp, + bool tosca_live, + bool expand_call) { + assert(ShenandoahSATBBarrier, "Should be checked by caller"); - if (ShenandoahSATBBarrier) { - satb_write_barrier_pre(masm, obj, pre_val, tmp, tosca_live, expand_call); - } -} - -void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register tmp, - bool tosca_live, - bool expand_call) { // If expand_call is true then we expand the call_VM_leaf macro // directly to skip generating the check by // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp. @@ -533,18 +523,18 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d assert_different_registers(dst, tmp1, r15_thread); // Generate the SATB pre-barrier code to log the value of // the referent field in an SATB buffer. - shenandoah_write_barrier_pre(masm /* masm */, - noreg /* obj */, - dst /* pre_val */, - tmp1 /* tmp */, - true /* tosca_live */, - true /* expand_call */); + satb_barrier(masm /* masm */, + noreg /* obj */, + dst /* pre_val */, + tmp1 /* tmp */, + true /* tosca_live */, + true /* expand_call */); restore_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ true); } } -void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj) { +void ShenandoahBarrierSetAssembler::card_barrier(MacroAssembler* masm, Register obj) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); // Does a store check for the oop in register obj. The content of @@ -575,41 +565,40 @@ void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register o void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) { - bool on_oop = is_reference_type(type); - bool in_heap = (decorators & IN_HEAP) != 0; - bool as_normal = (decorators & AS_NORMAL) != 0; - if (on_oop && in_heap) { - bool needs_pre_barrier = as_normal; + // 1: non-reference types require no barriers + if (!is_reference_type(type)) { + BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); + return; + } - // flatten object address if needed - // We do it regardless of precise because we need the registers - if (dst.index() == noreg && dst.disp() == 0) { - if (dst.base() != tmp1) { - __ movptr(tmp1, dst.base()); - } - } else { - __ lea(tmp1, dst); - } - - assert_different_registers(val, tmp1, tmp2, tmp3, r15_thread); - - if (needs_pre_barrier) { - shenandoah_write_barrier_pre(masm /*masm*/, - tmp1 /* obj */, - tmp2 /* pre_val */, - tmp3 /* tmp */, - val != noreg /* tosca_live */, - false /* expand_call */); - } - - BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg); - if (val != noreg) { - if (ShenandoahCardBarrier) { - store_check(masm, tmp1); - } + // Flatten object address right away for simplicity: likely needed by barriers + assert_different_registers(val, tmp1, tmp2, tmp3, r15_thread); + if (dst.index() == noreg && dst.disp() == 0) { + if (dst.base() != tmp1) { + __ movptr(tmp1, dst.base()); } } else { - BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); + __ lea(tmp1, dst); + } + + bool storing_non_null = (val != noreg); + + // 2: pre-barrier: SATB needs the previous value + if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) { + satb_barrier(masm, + tmp1 /* obj */, + tmp2 /* pre_val */, + tmp3 /* tmp */, + storing_non_null /* tosca_live */, + false /* expand_call */); + } + + // Store! + BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg); + + // 3: post-barrier: card barrier needs store address + if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) { + card_barrier(masm, tmp1); } } diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp index b0185f2dbff..b5cc5c8d834 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp @@ -41,21 +41,14 @@ class StubCodeGenerator; class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { private: - void satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register tmp, - bool tosca_live, - bool expand_call); + void satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register tmp, + bool tosca_live, + bool expand_call); - void shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register tmp, - bool tosca_live, - bool expand_call); - - void store_check(MacroAssembler* masm, Register obj); + void card_barrier(MacroAssembler* masm, Register obj); void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 747daefd51d..74df41f8682 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -143,7 +143,7 @@ class VM_Version_StubGenerator: public StubCodeGenerator { Label detect_486, cpu486, detect_586, std_cpuid1, std_cpuid4, std_cpuid24, std_cpuid29; Label sef_cpuid, sefsl1_cpuid, ext_cpuid, ext_cpuid1, ext_cpuid5, ext_cpuid7; - Label ext_cpuid8, done, wrapup, vector_save_restore, apx_save_restore_warning; + Label ext_cpuid8, done, wrapup, vector_save_restore, apx_save_restore_warning, apx_xstate; Label legacy_setup, save_restore_except, legacy_save_restore, start_simd_check; StubCodeMark mark(this, "VM_Version", "get_cpu_info_stub"); @@ -468,6 +468,20 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ movq(Address(rsi, 0), r16); __ movq(Address(rsi, 8), r31); + // + // Query CPUID 0xD.19 for APX XSAVE offset + // Extended State Enumeration Sub-leaf 19 (APX) + // EAX = size of APX state (should be 128) + // EBX = offset in standard XSAVE format + // + __ movl(rax, 0xD); + __ movl(rcx, 19); + __ cpuid(); + __ lea(rsi, Address(rbp, in_bytes(VM_Version::apx_xstate_size_offset()))); + __ movl(Address(rsi, 0), rax); + __ lea(rsi, Address(rbp, in_bytes(VM_Version::apx_xstate_offset_offset()))); + __ movl(Address(rsi, 0), rbx); + UseAPX = save_apx; __ bind(vector_save_restore); // @@ -921,8 +935,9 @@ void VM_Version::get_processor_features() { // Check if processor has Intel Ecore if (FLAG_IS_DEFAULT(EnableX86ECoreOpts) && is_intel() && is_intel_server_family() && - (_model == 0x97 || _model == 0xAA || _model == 0xAC || _model == 0xAF || - _model == 0xCC || _model == 0xDD)) { + (supports_hybrid() || + _model == 0xAF /* Xeon 6 E-cores (Sierra Forest) */ || + _model == 0xDD /* Xeon 6+ E-cores (Clearwater Forest) */ )) { FLAG_SET_DEFAULT(EnableX86ECoreOpts, true); } diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index cc93ee3564e..a3f2a801198 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -676,6 +676,10 @@ protected: // Space to save apx registers after signal handle jlong apx_save[2]; // Save r16 and r31 + // cpuid function 0xD, subleaf 19 (APX extended state) + uint32_t apx_xstate_size; // EAX: size of APX state (128) + uint32_t apx_xstate_offset; // EBX: offset in standard XSAVE area + VM_Features feature_flags() const; // Asserts @@ -739,6 +743,11 @@ public: static ByteSize ymm_save_offset() { return byte_offset_of(CpuidInfo, ymm_save); } static ByteSize zmm_save_offset() { return byte_offset_of(CpuidInfo, zmm_save); } static ByteSize apx_save_offset() { return byte_offset_of(CpuidInfo, apx_save); } + static ByteSize apx_xstate_offset_offset() { return byte_offset_of(CpuidInfo, apx_xstate_offset); } + static ByteSize apx_xstate_size_offset() { return byte_offset_of(CpuidInfo, apx_xstate_size); } + + static uint32_t apx_xstate_offset() { return _cpuid_info.apx_xstate_offset; } + static uint32_t apx_xstate_size() { return _cpuid_info.apx_xstate_size; } // The value used to check ymm register after signal handle static int ymm_test_value() { return 0xCAFEBABE; } diff --git a/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp b/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp index afef21b091a..3ab81697280 100644 --- a/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp +++ b/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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 @@ -412,12 +412,8 @@ run_stub: } void os::Aix::init_thread_fpu_state(void) { -#if !defined(USE_XLC_BUILTINS) // Disable FP exceptions. __asm__ __volatile__ ("mtfsfi 6,0"); -#else - __mtfsfi(6, 0); -#endif } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp b/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp index c741335b5f0..d9dac0e231f 100644 --- a/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp +++ b/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2013 SAP SE. All rights reserved. + * Copyright (c) 2012, 2026 SAP SE. 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,29 +29,21 @@ // Included in runtime/prefetch.inline.hpp inline void Prefetch::read(const void *loc, intx interval) { -#if !defined(USE_XLC_BUILTINS) __asm__ __volatile__ ( " dcbt 0, %0 \n" : : /*%0*/"r" ( ((address)loc) +((long)interval) ) //: ); -#else - __dcbt(((address)loc) +((long)interval)); -#endif } inline void Prefetch::write(void *loc, intx interval) { -#if !defined(USE_XLC_BUILTINS) __asm__ __volatile__ ( " dcbtst 0, %0 \n" : : /*%0*/"r" ( ((address)loc) +((long)interval) ) //: ); -#else - __dcbtst( ((address)loc) +((long)interval) ); -#endif } #endif // OS_CPU_AIX_PPC_PREFETCH_AIX_PPC_INLINE_HPP diff --git a/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp b/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp index 3dc0035ed87..d82b9d90417 100644 --- a/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp +++ b/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp @@ -43,7 +43,8 @@ void JavaThread::cache_global_variables() { BarrierSet* bs = BarrierSet::barrier_set(); if (bs->is_a(BarrierSet::CardTableBarrierSet)) { - _card_table_base = (address) (barrier_set_cast(bs)->card_table()->byte_map_base()); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); + _card_table_base = (address)ctbs->card_table_base_const(); } else { _card_table_base = nullptr; } diff --git a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp index 07f53582a76..ee08738c678 100644 --- a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp +++ b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp @@ -52,6 +52,7 @@ #include "utilities/debug.hpp" #include "utilities/events.hpp" #include "utilities/vmError.hpp" +#include "runtime/vm_version.hpp" // put OS-includes here # include @@ -380,6 +381,43 @@ size_t os::Posix::default_stack_size(os::ThreadType thr_type) { ///////////////////////////////////////////////////////////////////////////// // helper functions for fatal error handler +// XSAVE constants - from Intel SDM Vol. 1, Chapter 13 +#define XSAVE_HDR_OFFSET 512 +#define XFEATURE_APX (1ULL << 19) + +// XSAVE header structure +// See: Intel SDM Vol. 1, Section 13.4.2 "XSAVE Header" +// Also: Linux kernel arch/x86/include/asm/fpu/types.h +struct xstate_header { + uint64_t xfeatures; + uint64_t xcomp_bv; + uint64_t reserved[6]; +}; + +// APX extended state - R16-R31 (16 x 64-bit registers) +// See: Intel APX Architecture Specification +struct apx_state { + uint64_t regs[16]; // r16-r31 +}; + +static apx_state* get_apx_state(const ucontext_t* uc) { + uint32_t offset = VM_Version::apx_xstate_offset(); + if (offset == 0 || uc->uc_mcontext.fpregs == nullptr) { + return nullptr; + } + + char* xsave = (char*)uc->uc_mcontext.fpregs; + xstate_header* hdr = (xstate_header*)(xsave + XSAVE_HDR_OFFSET); + + // Check if APX state is present in this context + if (!(hdr->xfeatures & XFEATURE_APX)) { + return nullptr; + } + + return (apx_state*)(xsave + offset); +} + + void os::print_context(outputStream *st, const void *context) { if (context == nullptr) return; @@ -406,6 +444,14 @@ void os::print_context(outputStream *st, const void *context) { st->print(", R14=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_R14]); st->print(", R15=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_R15]); st->cr(); + // Dump APX EGPRs (R16-R31) + apx_state* apx = UseAPX ? get_apx_state(uc) : nullptr; + if (apx != nullptr) { + for (int i = 0; i < 16; i++) { + st->print("%sR%d=" INTPTR_FORMAT, (i % 4 == 0) ? "" : ", ", 16 + i, (intptr_t)apx->regs[i]); + if (i % 4 == 3) st->cr(); + } + } st->print( "RIP=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_RIP]); st->print(", EFLAGS=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_EFL]); st->print(", CSGSFS=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_CSGSFS]); @@ -432,37 +478,50 @@ void os::print_context(outputStream *st, const void *context) { } void os::print_register_info(outputStream *st, const void *context, int& continuation) { - const int register_count = 16; + if (context == nullptr) { + return; + } + const ucontext_t *uc = (const ucontext_t*)context; + apx_state* apx = UseAPX ? get_apx_state(uc) : nullptr; + + const int register_count = 16 + (apx != nullptr ? 16 : 0); int n = continuation; assert(n >= 0 && n <= register_count, "Invalid continuation value"); - if (context == nullptr || n == register_count) { + if (n == register_count) { return; } - const ucontext_t *uc = (const ucontext_t*)context; while (n < register_count) { // Update continuation with next index before printing location continuation = n + 1; + + if (n < 16) { + // Standard registers (RAX-R15) # define CASE_PRINT_REG(n, str, id) case n: st->print(str); print_location(st, uc->uc_mcontext.gregs[REG_##id]); - switch (n) { - CASE_PRINT_REG( 0, "RAX=", RAX); break; - CASE_PRINT_REG( 1, "RBX=", RBX); break; - CASE_PRINT_REG( 2, "RCX=", RCX); break; - CASE_PRINT_REG( 3, "RDX=", RDX); break; - CASE_PRINT_REG( 4, "RSP=", RSP); break; - CASE_PRINT_REG( 5, "RBP=", RBP); break; - CASE_PRINT_REG( 6, "RSI=", RSI); break; - CASE_PRINT_REG( 7, "RDI=", RDI); break; - CASE_PRINT_REG( 8, "R8 =", R8); break; - CASE_PRINT_REG( 9, "R9 =", R9); break; - CASE_PRINT_REG(10, "R10=", R10); break; - CASE_PRINT_REG(11, "R11=", R11); break; - CASE_PRINT_REG(12, "R12=", R12); break; - CASE_PRINT_REG(13, "R13=", R13); break; - CASE_PRINT_REG(14, "R14=", R14); break; - CASE_PRINT_REG(15, "R15=", R15); break; - } + switch (n) { + CASE_PRINT_REG( 0, "RAX=", RAX); break; + CASE_PRINT_REG( 1, "RBX=", RBX); break; + CASE_PRINT_REG( 2, "RCX=", RCX); break; + CASE_PRINT_REG( 3, "RDX=", RDX); break; + CASE_PRINT_REG( 4, "RSP=", RSP); break; + CASE_PRINT_REG( 5, "RBP=", RBP); break; + CASE_PRINT_REG( 6, "RSI=", RSI); break; + CASE_PRINT_REG( 7, "RDI=", RDI); break; + CASE_PRINT_REG( 8, "R8 =", R8); break; + CASE_PRINT_REG( 9, "R9 =", R9); break; + CASE_PRINT_REG(10, "R10=", R10); break; + CASE_PRINT_REG(11, "R11=", R11); break; + CASE_PRINT_REG(12, "R12=", R12); break; + CASE_PRINT_REG(13, "R13=", R13); break; + CASE_PRINT_REG(14, "R14=", R14); break; + CASE_PRINT_REG(15, "R15=", R15); break; + } # undef CASE_PRINT_REG + } else { + // APX extended general purpose registers (R16-R31) + st->print("R%d=", n); + print_location(st, apx->regs[n - 16]); + } ++n; } } diff --git a/test/jdk/javax/sql/rowset/serial/SerialClob/SetCharacterStream.java b/src/hotspot/share/cds/aotGrowableArray.cpp similarity index 62% rename from test/jdk/javax/sql/rowset/serial/SerialClob/SetCharacterStream.java rename to src/hotspot/share/cds/aotGrowableArray.cpp index be861e43723..ec63e7aa57f 100644 --- a/test/jdk/javax/sql/rowset/serial/SerialClob/SetCharacterStream.java +++ b/src/hotspot/share/cds/aotGrowableArray.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, 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 @@ -19,24 +19,16 @@ * 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. + * */ -import javax.sql.rowset.serial.SerialClob; -import javax.sql.rowset.serial.SerialException; -/** - * @test - * @bug 7077451 - * @summary tests if the correct exception is thrown when calling method setCharacterStream() on SerialClob - */ -public class SetCharacterStream { - - public static void main(String[] args) throws Exception { - SerialClob clob = new SerialClob(new char[0]); - try { - clob.setCharacterStream(0); - } catch (SerialException e) { - System.out.println("Test PASSED"); - } - } +#include "cds/aotGrowableArray.hpp" +#include "cds/aotMetaspace.hpp" +#include "memory/allocation.inline.hpp" +#include "utilities/growableArray.hpp" +void AOTGrowableArrayHelper::deallocate(void* mem) { + if (!AOTMetaspace::in_aot_cache(mem)) { + GrowableArrayCHeapAllocator::deallocate(mem); + } } diff --git a/src/hotspot/share/cds/aotGrowableArray.hpp b/src/hotspot/share/cds/aotGrowableArray.hpp new file mode 100644 index 00000000000..0a0c137ed07 --- /dev/null +++ b/src/hotspot/share/cds/aotGrowableArray.hpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2026, 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. + * + * 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. + * + */ + +#ifndef SHARE_AOT_AOTGROWABLEARRAY_HPP +#define SHARE_AOT_AOTGROWABLEARRAY_HPP + +#include +#include + +class AOTGrowableArrayHelper { +public: + static void deallocate(void* mem); +}; + +// An AOTGrowableArray provides the same functionality as a GrowableArray that +// uses the C heap allocator. In addition, AOTGrowableArray can be iterated with +// MetaspaceClosure. This type should be used for growable arrays that need to be +// stored in the AOT cache. See ModuleEntry::_reads for an example. +template +class AOTGrowableArray : public GrowableArrayWithAllocator> { + friend class VMStructs; + friend class GrowableArrayWithAllocator; + + static E* allocate(int max, MemTag mem_tag) { + return (E*)GrowableArrayCHeapAllocator::allocate(max, sizeof(E), mem_tag); + } + + E* allocate() { + return allocate(this->_capacity, mtClass); + } + + void deallocate(E* mem) { +#if INCLUDE_CDS + AOTGrowableArrayHelper::deallocate(mem); +#else + GrowableArrayCHeapAllocator::deallocate(mem); +#endif + } + +public: + AOTGrowableArray(int initial_capacity, MemTag mem_tag) : + GrowableArrayWithAllocator( + allocate(initial_capacity, mem_tag), + initial_capacity) {} + + AOTGrowableArray() : AOTGrowableArray(0, mtClassShared) {} + + // methods required by MetaspaceClosure + void metaspace_pointers_do(MetaspaceClosure* it); + int size_in_heapwords() const { return (int)heap_word_size(sizeof(*this)); } + MetaspaceClosureType type() const { return MetaspaceClosureType::GrowableArrayType; } + static bool is_read_only_by_default() { return false; } +}; + +#endif // SHARE_AOT_AOTGROWABLEARRAY_HPP diff --git a/test/jdk/javax/sql/rowset/serial/SerialBlob/SetBinaryStream.java b/src/hotspot/share/cds/aotGrowableArray.inline.hpp similarity index 62% rename from test/jdk/javax/sql/rowset/serial/SerialBlob/SetBinaryStream.java rename to src/hotspot/share/cds/aotGrowableArray.inline.hpp index a2d7ea2f8cb..8c6e8cb6503 100644 --- a/test/jdk/javax/sql/rowset/serial/SerialBlob/SetBinaryStream.java +++ b/src/hotspot/share/cds/aotGrowableArray.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, 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 @@ -19,24 +19,19 @@ * 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. + * */ -import javax.sql.rowset.serial.SerialBlob; -import javax.sql.rowset.serial.SerialException; -/** - * @test - * @bug 7077451 - * @summary tests if the correct exception is thrown when calling method setBinaryStream() on SerialBlob - */ -public class SetBinaryStream { +#ifndef SHARE_CDS_AOTGROWABLEARRAY_INLINE_HPP +#define SHARE_CDS_AOTGROWABLEARRAY_INLINE_HPP - public static void main(String[] args) throws Exception { - SerialBlob blob = new SerialBlob(new byte[0]); - try { - blob.setBinaryStream(0); - } catch (SerialException e) { - System.out.println("Test PASSED"); - } - } +#include "cds/aotGrowableArray.hpp" +#include "memory/metaspaceClosure.hpp" + +template +void AOTGrowableArray::metaspace_pointers_do(MetaspaceClosure* it) { + it->push_c_array(AOTGrowableArray::data_addr(), AOTGrowableArray::capacity()); } + +#endif // SHARE_CDS_AOTGROWABLEARRAY_INLINE_HPP diff --git a/src/hotspot/share/cds/aotMapLogger.cpp b/src/hotspot/share/cds/aotMapLogger.cpp index a252eae4b84..5e4e0956824 100644 --- a/src/hotspot/share/cds/aotMapLogger.cpp +++ b/src/hotspot/share/cds/aotMapLogger.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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,8 @@ #include "cds/aotStreamedHeapWriter.hpp" #include "cds/cdsConfig.hpp" #include "cds/filemap.hpp" +#include "classfile/moduleEntry.hpp" +#include "classfile/packageEntry.hpp" #include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "logging/log.hpp" @@ -141,7 +143,7 @@ public: info._buffered_addr = ref->obj(); info._requested_addr = ref->obj(); info._bytes = ref->size() * BytesPerWord; - info._type = ref->msotype(); + info._type = ref->type(); _objs.append(info); } @@ -214,7 +216,7 @@ void AOTMapLogger::dumptime_log_metaspace_region(const char* name, DumpRegion* r info._buffered_addr = src_info->buffered_addr(); info._requested_addr = info._buffered_addr + _buffer_to_requested_delta; info._bytes = src_info->size_in_bytes(); - info._type = src_info->msotype(); + info._type = src_info->type(); objs.append(info); } @@ -332,43 +334,52 @@ void AOTMapLogger::log_metaspace_objects_impl(address region_base, address regio address buffered_addr = info._buffered_addr; address requested_addr = info._requested_addr; int bytes = info._bytes; - MetaspaceObj::Type type = info._type; - const char* type_name = MetaspaceObj::type_name(type); + MetaspaceClosureType type = info._type; + const char* type_name = MetaspaceClosure::type_name(type); log_as_hex(last_obj_base, buffered_addr, last_obj_base + _buffer_to_requested_delta); switch (type) { - case MetaspaceObj::ClassType: + case MetaspaceClosureType::ClassType: log_klass((Klass*)src, requested_addr, type_name, bytes, current); break; - case MetaspaceObj::ConstantPoolType: + case MetaspaceClosureType::ConstantPoolType: log_constant_pool((ConstantPool*)src, requested_addr, type_name, bytes, current); break; - case MetaspaceObj::ConstantPoolCacheType: + case MetaspaceClosureType::ConstantPoolCacheType: log_constant_pool_cache((ConstantPoolCache*)src, requested_addr, type_name, bytes, current); break; - case MetaspaceObj::ConstMethodType: + case MetaspaceClosureType::ConstMethodType: log_const_method((ConstMethod*)src, requested_addr, type_name, bytes, current); break; - case MetaspaceObj::MethodType: + case MetaspaceClosureType::MethodType: log_method((Method*)src, requested_addr, type_name, bytes, current); break; - case MetaspaceObj::MethodCountersType: + case MetaspaceClosureType::MethodCountersType: log_method_counters((MethodCounters*)src, requested_addr, type_name, bytes, current); break; - case MetaspaceObj::MethodDataType: + case MetaspaceClosureType::MethodDataType: log_method_data((MethodData*)src, requested_addr, type_name, bytes, current); break; - case MetaspaceObj::SymbolType: + case MetaspaceClosureType::ModuleEntryType: + log_module_entry((ModuleEntry*)src, requested_addr, type_name, bytes, current); + break; + case MetaspaceClosureType::PackageEntryType: + log_package_entry((PackageEntry*)src, requested_addr, type_name, bytes, current); + break; + case MetaspaceClosureType::GrowableArrayType: + log_growable_array((GrowableArrayBase*)src, requested_addr, type_name, bytes, current); + break; + case MetaspaceClosureType::SymbolType: log_symbol((Symbol*)src, requested_addr, type_name, bytes, current); break; - case MetaspaceObj::KlassTrainingDataType: + case MetaspaceClosureType::KlassTrainingDataType: log_klass_training_data((KlassTrainingData*)src, requested_addr, type_name, bytes, current); break; - case MetaspaceObj::MethodTrainingDataType: + case MetaspaceClosureType::MethodTrainingDataType: log_method_training_data((MethodTrainingData*)src, requested_addr, type_name, bytes, current); break; - case MetaspaceObj::CompileTrainingDataType: + case MetaspaceClosureType::CompileTrainingDataType: log_compile_training_data((CompileTrainingData*)src, requested_addr, type_name, bytes, current); break; default: @@ -421,6 +432,27 @@ void AOTMapLogger::log_method_data(MethodData* md, address requested_addr, const log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes, md->method()->external_name()); } +void AOTMapLogger::log_module_entry(ModuleEntry* mod, address requested_addr, const char* type_name, + int bytes, Thread* current) { + ResourceMark rm(current); + log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes, + mod->name_as_C_string()); +} + +void AOTMapLogger::log_package_entry(PackageEntry* pkg, address requested_addr, const char* type_name, + int bytes, Thread* current) { + ResourceMark rm(current); + log_debug(aot, map)(_LOG_PREFIX " %s - %s", p2i(requested_addr), type_name, bytes, + pkg->module()->name_as_C_string(), pkg->name_as_C_string()); +} + +void AOTMapLogger::log_growable_array(GrowableArrayBase* arr, address requested_addr, const char* type_name, + int bytes, Thread* current) { + ResourceMark rm(current); + log_debug(aot, map)(_LOG_PREFIX " %d (%d)", p2i(requested_addr), type_name, bytes, + arr->length(), arr->capacity()); +} + void AOTMapLogger::log_klass(Klass* k, address requested_addr, const char* type_name, int bytes, Thread* current) { ResourceMark rm(current); diff --git a/src/hotspot/share/cds/aotMapLogger.hpp b/src/hotspot/share/cds/aotMapLogger.hpp index ba188514861..bf7ce0028b9 100644 --- a/src/hotspot/share/cds/aotMapLogger.hpp +++ b/src/hotspot/share/cds/aotMapLogger.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -28,6 +28,7 @@ #include "cds/archiveBuilder.hpp" #include "memory/allocation.hpp" #include "memory/allStatic.hpp" +#include "memory/metaspaceClosureType.hpp" #include "oops/oopsHierarchy.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" @@ -37,9 +38,13 @@ class ArchiveStreamedHeapInfo; class CompileTrainingData; class DumpRegion; class FileMapInfo; +class GrowableArrayBase; class KlassTrainingData; +class MethodCounters; class MethodTrainingData; +class ModuleEntry; class outputStream; +class PackageEntry; // Write detailed info to a mapfile to analyze contents of the AOT cache/CDS archive. // -Xlog:aot+map* can be used both when creating an AOT cache, or when using an AOT cache. @@ -62,7 +67,7 @@ class AOTMapLogger : AllStatic { address _buffered_addr; address _requested_addr; int _bytes; - MetaspaceObj::Type _type; + MetaspaceClosureType _type; }; public: @@ -142,6 +147,9 @@ private: Thread* current); static void log_klass(Klass* k, address requested_addr, const char* type_name, int bytes, Thread* current); static void log_method(Method* m, address requested_addr, const char* type_name, int bytes, Thread* current); + static void log_module_entry(ModuleEntry* mod, address requested_addr, const char* type_name, int bytes, Thread* current); + static void log_package_entry(PackageEntry* pkg, address requested_addr, const char* type_name, int bytes, Thread* current); + static void log_growable_array(GrowableArrayBase* arr, address requested_addr, const char* type_name, int bytes, Thread* current); static void log_symbol(Symbol* s, address requested_addr, const char* type_name, int bytes, Thread* current); static void log_klass_training_data(KlassTrainingData* ktd, address requested_addr, const char* type_name, int bytes, Thread* current); static void log_method_training_data(MethodTrainingData* mtd, address requested_addr, const char* type_name, int bytes, Thread* current); diff --git a/src/hotspot/share/cds/aotMappedHeapLoader.cpp b/src/hotspot/share/cds/aotMappedHeapLoader.cpp index 84051cbd9e5..210867be70c 100644 --- a/src/hotspot/share/cds/aotMappedHeapLoader.cpp +++ b/src/hotspot/share/cds/aotMappedHeapLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -360,10 +360,8 @@ bool AOTMappedHeapLoader::load_heap_region(FileMapInfo* mapinfo) { } objArrayOop AOTMappedHeapLoader::root_segment(int segment_idx) { - if (CDSConfig::is_dumping_heap()) { - assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread"); - } else { - assert(CDSConfig::is_using_archive(), "must be"); + if (!CDSConfig::is_using_archive()) { + assert(CDSConfig::is_dumping_heap() && Thread::current() == (Thread*)VMThread::vm_thread(), "sanity"); } objArrayOop segment = (objArrayOop)_root_segments->at(segment_idx).resolve(); @@ -466,7 +464,9 @@ void AOTMappedHeapLoader::finish_initialization(FileMapInfo* info) { add_root_segment((objArrayOop)segment_oop); } - StringTable::load_shared_strings_array(); + if (CDSConfig::is_dumping_final_static_archive()) { + StringTable::move_shared_strings_into_runtime_table(); + } } } @@ -619,7 +619,7 @@ bool AOTMappedHeapLoader::map_heap_region_impl(FileMapInfo* info) { aot_log_info(aot)("Preferred address to map heap data (to avoid relocation) is " INTPTR_FORMAT, p2i(requested_start)); // allocate from java heap - HeapWord* start = G1CollectedHeap::heap()->alloc_archive_region(word_size, (HeapWord*)requested_start); + HeapWord* start = G1CollectedHeap::heap()->alloc_archive_region(word_size); if (start == nullptr) { AOTMetaspace::report_loading_error("UseSharedSpaces: Unable to allocate java heap region for archive heap."); return false; diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 79d789e0c70..894a35183ca 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -698,6 +698,9 @@ public: Universe::metaspace_pointers_do(it); vmSymbols::metaspace_pointers_do(it); TrainingData::iterate_roots(it); + if (CDSConfig::is_dumping_full_module_graph()) { + ClassLoaderDataShared::iterate_roots(it); + } // The above code should find all the symbols that are referenced by the // archived classes. We just need to add the extra symbols which @@ -795,6 +798,10 @@ void VM_PopulateDumpSharedSpace::doit() { _builder.make_klasses_shareable(); AOTMetaspace::make_method_handle_intrinsics_shareable(); + if (CDSConfig::is_dumping_full_module_graph()) { + ClassLoaderDataShared::remove_unshareable_info(); + } + dump_java_heap_objects(); dump_shared_symbol_table(_builder.symbols()); @@ -1097,7 +1104,12 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS #if INCLUDE_CDS_JAVA_HEAP if (CDSConfig::is_dumping_heap()) { - assert(CDSConfig::allow_only_single_java_thread(), "Required"); + if (!CDSConfig::is_dumping_preimage_static_archive()) { + // A single thread is required for Reference handling and deterministic CDS archive. + // Its's not required for dumping preimage, where References won't be archived and + // determinism is not needed. + assert(CDSConfig::allow_only_single_java_thread(), "Required"); + } if (!HeapShared::is_archived_boot_layer_available(THREAD)) { report_loading_error("archivedBootLayer not available, disabling full module graph"); CDSConfig::stop_dumping_full_module_graph(); @@ -1135,6 +1147,7 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS HeapShared::init_heap_writer(); if (CDSConfig::is_dumping_full_module_graph()) { ClassLoaderDataShared::ensure_module_entry_tables_exist(); + ClassLoaderDataShared::build_tables(CHECK); HeapShared::reset_archived_object_states(CHECK); } @@ -1154,12 +1167,6 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS // Perhaps there is a way to avoid hard-coding these names here. // See discussion in JDK-8342481. } - - if (HeapShared::is_writing_mapping_mode()) { - // Do this at the very end, when no Java code will be executed. Otherwise - // some new strings may be added to the intern table. - StringTable::allocate_shared_strings_array(CHECK); - } } else { log_info(aot)("Not dumping heap, reset CDSConfig::_is_using_optimized_module_handling"); CDSConfig::stop_using_optimized_module_handling(); diff --git a/src/hotspot/share/cds/aotReferenceObjSupport.cpp b/src/hotspot/share/cds/aotReferenceObjSupport.cpp index aa7cc875533..0c27c8ce5f0 100644 --- a/src/hotspot/share/cds/aotReferenceObjSupport.cpp +++ b/src/hotspot/share/cds/aotReferenceObjSupport.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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,12 +177,17 @@ void AOTReferenceObjSupport::init_keep_alive_objs_table() { // Returns true IFF obj is an instance of java.lang.ref.Reference. If so, perform extra eligibility checks. bool AOTReferenceObjSupport::check_if_ref_obj(oop obj) { - // We have a single Java thread. This means java.lang.ref.Reference$ReferenceHandler thread - // is not running. Otherwise the checks for next/discovered may not work. - precond(CDSConfig::allow_only_single_java_thread()); assert_at_safepoint(); // _keep_alive_objs_table uses raw oops if (obj->klass()->is_subclass_of(vmClasses::Reference_klass())) { + // The following check works only if the java.lang.ref.Reference$ReferenceHandler thread + // is not running. + // + // This code is called on every object found by AOTArtifactFinder. When dumping the + // preimage archive, AOTArtifactFinder should not find any Reference objects. + precond(!CDSConfig::is_dumping_preimage_static_archive()); + precond(CDSConfig::allow_only_single_java_thread()); + precond(AOTReferenceObjSupport::is_enabled()); precond(JavaClasses::is_supported_for_archiving(obj)); precond(_keep_alive_objs_table != nullptr); diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index 328bed1ccfb..9161980c4be 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -243,7 +243,7 @@ bool ArchiveBuilder::gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool re if (get_follow_mode(ref) != make_a_copy) { return false; } - if (ref->msotype() == MetaspaceObj::ClassType) { + if (ref->type() == MetaspaceClosureType::ClassType) { Klass* klass = (Klass*)ref->obj(); assert(klass->is_klass(), "must be"); if (!is_excluded(klass)) { @@ -252,7 +252,7 @@ bool ArchiveBuilder::gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool re assert(klass->is_instance_klass(), "must be"); } } - } else if (ref->msotype() == MetaspaceObj::SymbolType) { + } else if (ref->type() == MetaspaceClosureType::SymbolType) { // Make sure the symbol won't be GC'ed while we are dumping the archive. Symbol* sym = (Symbol*)ref->obj(); sym->increment_refcount(); @@ -271,11 +271,6 @@ void ArchiveBuilder::gather_klasses_and_symbols() { aot_log_info(aot)("Gathering classes and symbols ... "); GatherKlassesAndSymbols doit(this); iterate_roots(&doit); -#if INCLUDE_CDS_JAVA_HEAP - if (CDSConfig::is_dumping_full_module_graph()) { - ClassLoaderDataShared::iterate_symbols(&doit); - } -#endif doit.finish(); if (CDSConfig::is_dumping_static_archive()) { @@ -446,14 +441,14 @@ bool ArchiveBuilder::gather_one_source_obj(MetaspaceClosure::Ref* ref, bool read } #ifdef ASSERT - if (ref->msotype() == MetaspaceObj::MethodType) { + if (ref->type() == MetaspaceClosureType::MethodType) { Method* m = (Method*)ref->obj(); assert(!RegeneratedClasses::has_been_regenerated((address)m->method_holder()), "Should not archive methods in a class that has been regenerated"); } #endif - if (ref->msotype() == MetaspaceObj::MethodDataType) { + if (ref->type() == MetaspaceClosureType::MethodDataType) { MethodData* md = (MethodData*)ref->obj(); md->clean_method_data(false /* always_clean */); } @@ -554,16 +549,16 @@ ArchiveBuilder::FollowMode ArchiveBuilder::get_follow_mode(MetaspaceClosure::Ref if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(obj)) { // Don't dump existing shared metadata again. return point_to_it; - } else if (ref->msotype() == MetaspaceObj::MethodDataType || - ref->msotype() == MetaspaceObj::MethodCountersType || - ref->msotype() == MetaspaceObj::KlassTrainingDataType || - ref->msotype() == MetaspaceObj::MethodTrainingDataType || - ref->msotype() == MetaspaceObj::CompileTrainingDataType) { + } else if (ref->type() == MetaspaceClosureType::MethodDataType || + ref->type() == MetaspaceClosureType::MethodCountersType || + ref->type() == MetaspaceClosureType::KlassTrainingDataType || + ref->type() == MetaspaceClosureType::MethodTrainingDataType || + ref->type() == MetaspaceClosureType::CompileTrainingDataType) { return (TrainingData::need_data() || TrainingData::assembling_data()) ? make_a_copy : set_to_null; - } else if (ref->msotype() == MetaspaceObj::AdapterHandlerEntryType) { + } else if (ref->type() == MetaspaceClosureType::AdapterHandlerEntryType) { return CDSConfig::is_dumping_adapters() ? make_a_copy : set_to_null; } else { - if (ref->msotype() == MetaspaceObj::ClassType) { + if (ref->type() == MetaspaceClosureType::ClassType) { Klass* klass = (Klass*)ref->obj(); assert(klass->is_klass(), "must be"); if (RegeneratedClasses::has_been_regenerated(klass)) { @@ -620,15 +615,6 @@ void ArchiveBuilder::dump_rw_metadata() { ResourceMark rm; aot_log_info(aot)("Allocating RW objects ... "); make_shallow_copies(&_rw_region, &_rw_src_objs); - -#if INCLUDE_CDS_JAVA_HEAP - if (CDSConfig::is_dumping_full_module_graph()) { - // Archive the ModuleEntry's and PackageEntry's of the 3 built-in loaders - char* start = rw_region()->top(); - ClassLoaderDataShared::allocate_archived_tables(); - alloc_stats()->record_modules(rw_region()->top() - start, /*read_only*/false); - } -#endif } void ArchiveBuilder::dump_ro_metadata() { @@ -637,15 +623,6 @@ void ArchiveBuilder::dump_ro_metadata() { start_dump_region(&_ro_region); make_shallow_copies(&_ro_region, &_ro_src_objs); - -#if INCLUDE_CDS_JAVA_HEAP - if (CDSConfig::is_dumping_full_module_graph()) { - char* start = ro_region()->top(); - ClassLoaderDataShared::init_archived_tables(); - alloc_stats()->record_modules(ro_region()->top() - start, /*read_only*/true); - } -#endif - RegeneratedClasses::record_regenerated_objects(); } @@ -663,7 +640,7 @@ void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* s size_t alignment = SharedSpaceObjectAlignment; // alignment for the dest pointer char* oldtop = dump_region->top(); - if (src_info->msotype() == MetaspaceObj::ClassType) { + if (src_info->type() == MetaspaceClosureType::ClassType) { // Allocate space for a pointer directly in front of the future InstanceKlass, so // we can do a quick lookup from InstanceKlass* -> RunTimeClassInfo* // without building another hashtable. See RunTimeClassInfo::get_for() @@ -679,7 +656,7 @@ void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* s alignment = nth_bit(ArchiveBuilder::precomputed_narrow_klass_shift()); } #endif - } else if (src_info->msotype() == MetaspaceObj::SymbolType) { + } else if (src_info->type() == MetaspaceClosureType::SymbolType) { // Symbols may be allocated by using AllocateHeap, so their sizes // may be less than size_in_bytes() indicates. bytes = ((Symbol*)src)->byte_size(); @@ -689,7 +666,7 @@ void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* s memcpy(dest, src, bytes); // Update the hash of buffered sorted symbols for static dump so that the symbols have deterministic contents - if (CDSConfig::is_dumping_static_archive() && (src_info->msotype() == MetaspaceObj::SymbolType)) { + if (CDSConfig::is_dumping_static_archive() && (src_info->type() == MetaspaceClosureType::SymbolType)) { Symbol* buffered_symbol = (Symbol*)dest; assert(((Symbol*)src)->is_permanent(), "archived symbols must be permanent"); buffered_symbol->update_identity_hash(); @@ -704,7 +681,7 @@ void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* s } } - intptr_t* archived_vtable = CppVtables::get_archived_vtable(src_info->msotype(), (address)dest); + intptr_t* archived_vtable = CppVtables::get_archived_vtable(src_info->type(), (address)dest); if (archived_vtable != nullptr) { *(address*)dest = (address)archived_vtable; ArchivePtrMarker::mark_pointer((address*)dest); @@ -714,7 +691,7 @@ void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* s src_info->set_buffered_addr((address)dest); char* newtop = dump_region->top(); - _alloc_stats.record(src_info->msotype(), int(newtop - oldtop), src_info->read_only()); + _alloc_stats.record(src_info->type(), int(newtop - oldtop), src_info->read_only()); DEBUG_ONLY(_alloc_stats.verify((int)dump_region->used(), src_info->read_only())); } @@ -997,15 +974,15 @@ void ArchiveBuilder::make_training_data_shareable() { return; } - if (info.msotype() == MetaspaceObj::KlassTrainingDataType || - info.msotype() == MetaspaceObj::MethodTrainingDataType || - info.msotype() == MetaspaceObj::CompileTrainingDataType) { + if (info.type() == MetaspaceClosureType::KlassTrainingDataType || + info.type() == MetaspaceClosureType::MethodTrainingDataType || + info.type() == MetaspaceClosureType::CompileTrainingDataType) { TrainingData* buffered_td = (TrainingData*)info.buffered_addr(); buffered_td->remove_unshareable_info(); - } else if (info.msotype() == MetaspaceObj::MethodDataType) { + } else if (info.type() == MetaspaceClosureType::MethodDataType) { MethodData* buffered_mdo = (MethodData*)info.buffered_addr(); buffered_mdo->remove_unshareable_info(); - } else if (info.msotype() == MetaspaceObj::MethodCountersType) { + } else if (info.type() == MetaspaceClosureType::MethodCountersType) { MethodCounters* buffered_mc = (MethodCounters*)info.buffered_addr(); buffered_mc->remove_unshareable_info(); } diff --git a/src/hotspot/share/cds/archiveBuilder.hpp b/src/hotspot/share/cds/archiveBuilder.hpp index 9a628439039..9de6c02edc5 100644 --- a/src/hotspot/share/cds/archiveBuilder.hpp +++ b/src/hotspot/share/cds/archiveBuilder.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -134,13 +134,13 @@ private: int _size_in_bytes; int _id; // Each object has a unique serial ID, starting from zero. The ID is assigned // when the object is added into _source_objs. - MetaspaceObj::Type _msotype; + MetaspaceClosureType _type; address _source_addr; // The source object to be copied. address _buffered_addr; // The copy of this object insider the buffer. public: SourceObjInfo(MetaspaceClosure::Ref* ref, bool read_only, FollowMode follow_mode) : _ptrmap_start(0), _ptrmap_end(0), _read_only(read_only), _has_embedded_pointer(false), _follow_mode(follow_mode), - _size_in_bytes(ref->size() * BytesPerWord), _id(0), _msotype(ref->msotype()), + _size_in_bytes(ref->size() * BytesPerWord), _id(0), _type(ref->type()), _source_addr(ref->obj()) { if (follow_mode == point_to_it) { _buffered_addr = ref->obj(); @@ -155,7 +155,7 @@ private: SourceObjInfo(address src, SourceObjInfo* renegerated_obj_info) : _ptrmap_start(0), _ptrmap_end(0), _read_only(false), _follow_mode(renegerated_obj_info->_follow_mode), - _size_in_bytes(0), _msotype(renegerated_obj_info->_msotype), + _size_in_bytes(0), _type(renegerated_obj_info->_type), _source_addr(src), _buffered_addr(renegerated_obj_info->_buffered_addr) {} bool should_copy() const { return _follow_mode == make_a_copy; } @@ -182,7 +182,7 @@ private: } return _buffered_addr; } - MetaspaceObj::Type msotype() const { return _msotype; } + MetaspaceClosureType type() const { return _type; } FollowMode follow_mode() const { return _follow_mode; } }; diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 5f6b568dd6e..f4ef3c66f7a 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -556,7 +556,9 @@ void CDSConfig::check_aotmode_record() { // At VM exit, the module graph may be contaminated with program states. // We will rebuild the module graph when dumping the CDS final image. - disable_heap_dumping(); + _is_using_optimized_module_handling = false; + _is_using_full_module_graph = false; + _is_dumping_full_module_graph = false; } void CDSConfig::check_aotmode_create() { @@ -582,6 +584,7 @@ void CDSConfig::check_aotmode_create() { substitute_aot_filename(FLAG_MEMBER_ENUM(AOTCache)); _is_dumping_final_static_archive = true; + _is_using_full_module_graph = false; UseSharedSpaces = true; RequireSharedSpaces = true; @@ -954,7 +957,9 @@ bool CDSConfig::are_vm_options_incompatible_with_dumping_heap() { } bool CDSConfig::is_dumping_heap() { - if (!(is_dumping_classic_static_archive() || is_dumping_final_static_archive()) + // Note: when dumping preimage static archive, only a very limited set of oops + // are dumped. + if (!is_dumping_static_archive() || are_vm_options_incompatible_with_dumping_heap() || _disable_heap_dumping) { return false; @@ -966,6 +971,26 @@ bool CDSConfig::is_loading_heap() { return HeapShared::is_archived_heap_in_use(); } +bool CDSConfig::is_dumping_klass_subgraphs() { + if (is_dumping_classic_static_archive() || is_dumping_final_static_archive()) { + // KlassSubGraphs (see heapShared.cpp) is a legacy mechanism for archiving oops. It + // has been superceded by AOT class linking. This feature is used only when + // AOT class linking is disabled. + // + // KlassSubGraphs are disabled in the preimage static archive, which contains a very + // limited set of oops. + return is_dumping_heap() && !is_dumping_aot_linked_classes(); + } else { + return false; + } +} + +bool CDSConfig::is_using_klass_subgraphs() { + return (is_loading_heap() && + !CDSConfig::is_using_aot_linked_classes() && + !CDSConfig::is_dumping_final_static_archive()); +} + bool CDSConfig::is_using_full_module_graph() { if (ClassLoaderDataShared::is_full_module_graph_loaded()) { return true; diff --git a/src/hotspot/share/cds/cdsConfig.hpp b/src/hotspot/share/cds/cdsConfig.hpp index 202904e8231..739dbb4937b 100644 --- a/src/hotspot/share/cds/cdsConfig.hpp +++ b/src/hotspot/share/cds/cdsConfig.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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,9 @@ public: static bool is_dumping_heap() NOT_CDS_JAVA_HEAP_RETURN_(false); static bool is_loading_heap() NOT_CDS_JAVA_HEAP_RETURN_(false); + static bool is_dumping_klass_subgraphs() NOT_CDS_JAVA_HEAP_RETURN_(false); + static bool is_using_klass_subgraphs() NOT_CDS_JAVA_HEAP_RETURN_(false); + static bool is_dumping_invokedynamic() NOT_CDS_JAVA_HEAP_RETURN_(false); static bool is_dumping_method_handles() NOT_CDS_JAVA_HEAP_RETURN_(false); diff --git a/src/hotspot/share/cds/cppVtables.cpp b/src/hotspot/share/cds/cppVtables.cpp index f2862454286..da68fa70761 100644 --- a/src/hotspot/share/cds/cppVtables.cpp +++ b/src/hotspot/share/cds/cppVtables.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -22,12 +22,14 @@ * */ +#include "cds/aotGrowableArray.hpp" #include "cds/aotMetaspace.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" #include "cds/cppVtables.hpp" #include "logging/log.hpp" +#include "memory/resourceArea.hpp" #include "oops/instanceClassLoaderKlass.hpp" #include "oops/instanceMirrorKlass.hpp" #include "oops/instanceRefKlass.hpp" @@ -53,6 +55,19 @@ // + at run time: we clone the actual contents of the vtables from libjvm.so // into our own tables. + +#ifndef PRODUCT + +// AOTGrowableArray has a vtable only when in non-product builds (due to +// the virtual printing functions in AnyObj). + +using GrowableArray_ModuleEntry_ptr = AOTGrowableArray; + +#define DEBUG_CPP_VTABLE_TYPES_DO(f) \ + f(GrowableArray_ModuleEntry_ptr) \ + +#endif + // Currently, the archive contains ONLY the following types of objects that have C++ vtables. #define CPP_VTABLE_TYPES_DO(f) \ f(ConstantPool) \ @@ -68,7 +83,8 @@ f(TypeArrayKlass) \ f(KlassTrainingData) \ f(MethodTrainingData) \ - f(CompileTrainingData) + f(CompileTrainingData) \ + NOT_PRODUCT(DEBUG_CPP_VTABLE_TYPES_DO(f)) class CppVtableInfo { intptr_t _vtable_size; @@ -86,7 +102,7 @@ public: } }; -static inline intptr_t* vtable_of(const Metadata* m) { +static inline intptr_t* vtable_of(const void* m) { return *((intptr_t**)m); } @@ -116,6 +132,7 @@ CppVtableInfo* CppVtableCloner::allocate_and_initialize(const char* name) { template void CppVtableCloner::initialize(const char* name, CppVtableInfo* info) { + ResourceMark rm; T tmp; // Allocate temporary dummy metadata object to get to the original vtable. int n = info->vtable_size(); intptr_t* srcvtable = vtable_of(&tmp); @@ -268,7 +285,7 @@ void CppVtables::serialize(SerializeClosure* soc) { } } -intptr_t* CppVtables::get_archived_vtable(MetaspaceObj::Type msotype, address obj) { +intptr_t* CppVtables::get_archived_vtable(MetaspaceClosureType type, address obj) { if (!_orig_cpp_vtptrs_inited) { CPP_VTABLE_TYPES_DO(INIT_ORIG_CPP_VTPTRS); _orig_cpp_vtptrs_inited = true; @@ -276,19 +293,23 @@ intptr_t* CppVtables::get_archived_vtable(MetaspaceObj::Type msotype, address ob assert(CDSConfig::is_dumping_archive(), "sanity"); int kind = -1; - switch (msotype) { - case MetaspaceObj::SymbolType: - case MetaspaceObj::TypeArrayU1Type: - case MetaspaceObj::TypeArrayU2Type: - case MetaspaceObj::TypeArrayU4Type: - case MetaspaceObj::TypeArrayU8Type: - case MetaspaceObj::TypeArrayOtherType: - case MetaspaceObj::ConstMethodType: - case MetaspaceObj::ConstantPoolCacheType: - case MetaspaceObj::AnnotationsType: - case MetaspaceObj::RecordComponentType: - case MetaspaceObj::AdapterHandlerEntryType: - case MetaspaceObj::AdapterFingerPrintType: + switch (type) { + case MetaspaceClosureType::SymbolType: + case MetaspaceClosureType::TypeArrayU1Type: + case MetaspaceClosureType::TypeArrayU2Type: + case MetaspaceClosureType::TypeArrayU4Type: + case MetaspaceClosureType::TypeArrayU8Type: + case MetaspaceClosureType::TypeArrayOtherType: + case MetaspaceClosureType::CArrayType: + case MetaspaceClosureType::ConstMethodType: + case MetaspaceClosureType::ConstantPoolCacheType: + case MetaspaceClosureType::AnnotationsType: + case MetaspaceClosureType::ModuleEntryType: + case MetaspaceClosureType::PackageEntryType: + case MetaspaceClosureType::RecordComponentType: + case MetaspaceClosureType::AdapterHandlerEntryType: + case MetaspaceClosureType::AdapterFingerPrintType: + PRODUCT_ONLY(case MetaspaceClosureType::GrowableArrayType:) // These have no vtables. break; default: diff --git a/src/hotspot/share/cds/cppVtables.hpp b/src/hotspot/share/cds/cppVtables.hpp index b40ca036023..9e28ba020ee 100644 --- a/src/hotspot/share/cds/cppVtables.hpp +++ b/src/hotspot/share/cds/cppVtables.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -27,6 +27,7 @@ #include "memory/allocation.hpp" #include "memory/allStatic.hpp" +#include "memory/metaspaceClosureType.hpp" #include "utilities/globalDefinitions.hpp" class ArchiveBuilder; @@ -40,7 +41,7 @@ class CppVtables : AllStatic { public: static void dumptime_init(ArchiveBuilder* builder); static void zero_archived_vtables(); - static intptr_t* get_archived_vtable(MetaspaceObj::Type msotype, address obj); + static intptr_t* get_archived_vtable(MetaspaceClosureType type, address obj); static void serialize(SerializeClosure* sc); static bool is_valid_shared_method(const Method* m) NOT_CDS_RETURN_(false); static char* vtables_serialized_base() { return _vtables_serialized_base; } diff --git a/src/hotspot/share/cds/dumpAllocStats.hpp b/src/hotspot/share/cds/dumpAllocStats.hpp index 7d651320e6f..4553f0f6a01 100644 --- a/src/hotspot/share/cds/dumpAllocStats.hpp +++ b/src/hotspot/share/cds/dumpAllocStats.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -27,32 +27,34 @@ #include "classfile/compactHashtable.hpp" #include "memory/allocation.hpp" +#include "memory/metaspaceClosureType.hpp" // This is for dumping detailed statistics for the allocations // in the shared spaces. class DumpAllocStats : public StackObj { public: - // Here's poor man's enum inheritance -#define SHAREDSPACE_OBJ_TYPES_DO(f) \ - METASPACE_OBJ_TYPES_DO(f) \ +#define DUMPED_OBJ_TYPES_DO(f) \ + METASPACE_CLOSURE_TYPES_DO(f) \ f(SymbolHashentry) \ f(SymbolBucket) \ f(StringHashentry) \ f(StringBucket) \ - f(ModulesNatives) \ f(CppVTables) \ f(Other) +#define DUMPED_TYPE_DECLARE(name) name ## Type, +#define DUMPED_TYPE_NAME_CASE(name) case name ## Type: return #name; + enum Type { // Types are MetaspaceObj::ClassType, MetaspaceObj::SymbolType, etc - SHAREDSPACE_OBJ_TYPES_DO(METASPACE_OBJ_TYPE_DECLARE) + DUMPED_OBJ_TYPES_DO(DUMPED_TYPE_DECLARE) _number_of_types }; static const char* type_name(Type type) { switch(type) { - SHAREDSPACE_OBJ_TYPES_DO(METASPACE_OBJ_TYPE_NAME_CASE) + DUMPED_OBJ_TYPES_DO(DUMPED_TYPE_NAME_CASE) default: ShouldNotReachHere(); return nullptr; @@ -101,16 +103,12 @@ public: CompactHashtableStats* symbol_stats() { return &_symbol_stats; } CompactHashtableStats* string_stats() { return &_string_stats; } - void record(MetaspaceObj::Type type, int byte_size, bool read_only) { - assert(int(type) >= 0 && type < MetaspaceObj::_number_of_types, "sanity"); + void record(MetaspaceClosureType type, int byte_size, bool read_only) { + int t = (int)type; + assert(t >= 0 && t < (int)MetaspaceClosureType::_number_of_types, "sanity"); int which = (read_only) ? RO : RW; - _counts[which][type] ++; - _bytes [which][type] += byte_size; - } - - void record_modules(int byte_size, bool read_only) { - int which = (read_only) ? RO : RW; - _bytes [which][ModulesNativesType] += byte_size; + _counts[which][t] ++; + _bytes [which][t] += byte_size; } void record_other_type(int byte_size, bool read_only) { diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index fdc335f3799..143f9147853 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -210,7 +210,7 @@ static bool is_subgraph_root_class_of(ArchivableStaticFieldInfo fields[], Instan bool HeapShared::is_subgraph_root_class(InstanceKlass* ik) { assert(CDSConfig::is_dumping_heap(), "dump-time only"); - if (!CDSConfig::is_dumping_aot_linked_classes()) { + if (CDSConfig::is_dumping_klass_subgraphs()) { // Legacy CDS archive support (to be deprecated) return is_subgraph_root_class_of(archive_subgraph_entry_fields, ik) || is_subgraph_root_class_of(fmg_archive_subgraph_entry_fields, ik); @@ -413,6 +413,8 @@ void HeapShared::materialize_thread_object() { void HeapShared::add_to_dumped_interned_strings(oop string) { assert(HeapShared::is_writing_mapping_mode(), "Only used by this mode"); AOTMappedHeapWriter::add_to_dumped_interned_strings(string); + bool success = archive_reachable_objects_from(1, _dump_time_special_subgraph, string); + assert(success, "shared strings array must not point to arrays or strings that are too large to archive"); } void HeapShared::finalize_initialization(FileMapInfo* static_mapinfo) { @@ -453,7 +455,6 @@ int HeapShared::append_root(oop obj) { oop HeapShared::get_root(int index, bool clear) { assert(index >= 0, "sanity"); - assert(!CDSConfig::is_dumping_heap() && CDSConfig::is_using_archive(), "runtime only"); assert(is_archived_heap_in_use(), "getting roots into heap that is not used"); oop result; @@ -598,8 +599,7 @@ public: void set_oop(MetaspaceObj* ptr, oop o) { MutexLocker ml(ScratchObjects_lock, Mutex::_no_safepoint_check_flag); OopHandle handle(Universe::vm_global(), o); - bool is_new = put(ptr, handle); - assert(is_new, "cannot set twice"); + put_when_absent(ptr, handle); } void remove_oop(MetaspaceObj* ptr) { MutexLocker ml(ScratchObjects_lock, Mutex::_no_safepoint_check_flag); @@ -612,6 +612,11 @@ public: }; void HeapShared::add_scratch_resolved_references(ConstantPool* src, objArrayOop dest) { + if (CDSConfig::is_dumping_preimage_static_archive() && scratch_resolved_references(src) != nullptr) { + // We are in AOT training run. The class has been redefined and we are giving it a new resolved_reference. + // Ignore it, as this class will be excluded from the AOT config. + return; + } if (SystemDictionaryShared::is_builtin_loader(src->pool_holder()->class_loader_data())) { _scratch_objects_table->set_oop(src, dest); } @@ -831,14 +836,6 @@ static objArrayOop get_archived_resolved_references(InstanceKlass* src_ik) { return nullptr; } -void HeapShared::archive_strings() { - assert(HeapShared::is_writing_mapping_mode(), "should not reach here"); - oop shared_strings_array = StringTable::init_shared_strings_array(); - bool success = archive_reachable_objects_from(1, _dump_time_special_subgraph, shared_strings_array); - assert(success, "shared strings array must not point to arrays or strings that are too large to archive"); - StringTable::set_shared_strings_array_index(append_root(shared_strings_array)); -} - int HeapShared::archive_exception_instance(oop exception) { bool success = archive_reachable_objects_from(1, _dump_time_special_subgraph, exception); assert(success, "sanity"); @@ -890,7 +887,7 @@ void HeapShared::start_scanning_for_oops() { void HeapShared::end_scanning_for_oops() { if (is_writing_mapping_mode()) { - archive_strings(); + StringTable::init_shared_table(); } delete_seen_objects_table(); } @@ -940,7 +937,7 @@ void HeapShared::scan_java_class(Klass* orig_k) { void HeapShared::archive_subgraphs() { assert(CDSConfig::is_dumping_heap(), "must be"); - if (!CDSConfig::is_dumping_aot_linked_classes()) { + if (CDSConfig::is_dumping_klass_subgraphs()) { archive_object_subgraphs(archive_subgraph_entry_fields, false /* is_full_module_graph */); if (CDSConfig::is_dumping_full_module_graph()) { @@ -948,10 +945,6 @@ void HeapShared::archive_subgraphs() { true /* is_full_module_graph */); } } - - if (CDSConfig::is_dumping_full_module_graph()) { - Modules::verify_archived_modules(); - } } // @@ -1302,10 +1295,7 @@ static void verify_the_heap(Klass* k, const char* which) { // this case, we will not load the ArchivedKlassSubGraphInfoRecord and will clear its roots. void HeapShared::resolve_classes(JavaThread* current) { assert(CDSConfig::is_using_archive(), "runtime only!"); - if (!is_archived_heap_in_use()) { - return; // nothing to do - } - if (!CDSConfig::is_using_aot_linked_classes()) { + if (CDSConfig::is_using_klass_subgraphs()) { resolve_classes_for_subgraphs(current, archive_subgraph_entry_fields); resolve_classes_for_subgraphs(current, fmg_archive_subgraph_entry_fields); } @@ -1395,7 +1385,7 @@ void HeapShared::init_classes_for_special_subgraph(Handle class_loader, TRAPS) { void HeapShared::initialize_from_archived_subgraph(JavaThread* current, Klass* k) { JavaThread* THREAD = current; - if (!is_archived_heap_in_use()) { + if (!CDSConfig::is_using_klass_subgraphs()) { return; // nothing to do } @@ -1871,7 +1861,7 @@ void HeapShared::archive_reachable_objects_from_static_field(InstanceKlass *k, const char* klass_name, int field_offset, const char* field_name) { - assert(CDSConfig::is_dumping_heap(), "dump time only"); + precond(CDSConfig::is_dumping_klass_subgraphs()); assert(k->defined_by_boot_loader(), "must be boot class"); oop m = k->java_mirror(); @@ -1922,7 +1912,7 @@ class VerifySharedOopClosure: public BasicOopIterateClosure { }; void HeapShared::verify_subgraph_from_static_field(InstanceKlass* k, int field_offset) { - assert(CDSConfig::is_dumping_heap(), "dump time only"); + precond(CDSConfig::is_dumping_klass_subgraphs()); assert(k->defined_by_boot_loader(), "must be boot class"); oop m = k->java_mirror(); @@ -2148,7 +2138,7 @@ void HeapShared::init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[], void HeapShared::init_subgraph_entry_fields(TRAPS) { assert(CDSConfig::is_dumping_heap(), "must be"); _dump_time_subgraph_info_table = new (mtClass)DumpTimeKlassSubGraphInfoTable(); - if (!CDSConfig::is_dumping_aot_linked_classes()) { + if (CDSConfig::is_dumping_klass_subgraphs()) { init_subgraph_entry_fields(archive_subgraph_entry_fields, CHECK); if (CDSConfig::is_dumping_full_module_graph()) { init_subgraph_entry_fields(fmg_archive_subgraph_entry_fields, CHECK); diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index 118c60faa60..3c7068e96ab 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -478,7 +478,6 @@ private: static bool has_been_archived(oop orig_obj); static void prepare_resolved_references(); - static void archive_strings(); static void archive_subgraphs(); static void copy_java_mirror(oop orig_mirror, oop scratch_m); diff --git a/src/hotspot/share/ci/ciField.cpp b/src/hotspot/share/ci/ciField.cpp index 19e05784f4d..e0c818f02fc 100644 --- a/src/hotspot/share/ci/ciField.cpp +++ b/src/hotspot/share/ci/ciField.cpp @@ -216,6 +216,10 @@ ciField::ciField(fieldDescriptor *fd) : static bool trust_final_non_static_fields(ciInstanceKlass* holder) { if (holder == nullptr) return false; + if (holder->trust_final_fields()) { + // Explicit opt-in from system classes + return true; + } // Even if general trusting is disabled, trust system-built closures in these packages. if (holder->is_in_package("java/lang/invoke") || holder->is_in_package("sun/invoke") || holder->is_in_package("java/lang/reflect") || holder->is_in_package("jdk/internal/reflect") || @@ -230,14 +234,6 @@ static bool trust_final_non_static_fields(ciInstanceKlass* holder) { // Trust final fields in records if (holder->is_record()) return true; - // Trust Atomic*FieldUpdaters: they are very important for performance, and make up one - // more reason not to use Unsafe, if their final fields are trusted. See more in JDK-8140483. - if (holder->name() == ciSymbols::java_util_concurrent_atomic_AtomicIntegerFieldUpdater_Impl() || - holder->name() == ciSymbols::java_util_concurrent_atomic_AtomicLongFieldUpdater_CASUpdater() || - holder->name() == ciSymbols::java_util_concurrent_atomic_AtomicLongFieldUpdater_LockedUpdater() || - holder->name() == ciSymbols::java_util_concurrent_atomic_AtomicReferenceFieldUpdater_Impl()) { - return true; - } return TrustFinalNonStaticFields; } diff --git a/src/hotspot/share/ci/ciInstanceKlass.cpp b/src/hotspot/share/ci/ciInstanceKlass.cpp index 64b9acf9146..33bcabc4566 100644 --- a/src/hotspot/share/ci/ciInstanceKlass.cpp +++ b/src/hotspot/share/ci/ciInstanceKlass.cpp @@ -65,6 +65,7 @@ ciInstanceKlass::ciInstanceKlass(Klass* k) : _has_nonstatic_concrete_methods = ik->has_nonstatic_concrete_methods(); _is_hidden = ik->is_hidden(); _is_record = ik->is_record(); + _trust_final_fields = ik->trust_final_fields(); _nonstatic_fields = nullptr; // initialized lazily by compute_nonstatic_fields: _has_injected_fields = -1; _implementor = nullptr; // we will fill these lazily diff --git a/src/hotspot/share/ci/ciInstanceKlass.hpp b/src/hotspot/share/ci/ciInstanceKlass.hpp index a1b2d8dd12d..8ccf1fadfb7 100644 --- a/src/hotspot/share/ci/ciInstanceKlass.hpp +++ b/src/hotspot/share/ci/ciInstanceKlass.hpp @@ -59,6 +59,7 @@ private: bool _has_nonstatic_concrete_methods; bool _is_hidden; bool _is_record; + bool _trust_final_fields; bool _has_trusted_loader; ciFlags _flags; @@ -207,6 +208,10 @@ public: return _is_record; } + bool trust_final_fields() const { + return _trust_final_fields; + } + ciInstanceKlass* get_canonical_holder(int offset); ciField* get_field_by_offset(int field_offset, bool is_static); ciField* get_field_by_name(ciSymbol* name, ciSymbol* signature, bool is_static); diff --git a/src/hotspot/share/ci/ciUtilities.cpp b/src/hotspot/share/ci/ciUtilities.cpp index 0c5b4d9824f..011fa049275 100644 --- a/src/hotspot/share/ci/ciUtilities.cpp +++ b/src/hotspot/share/ci/ciUtilities.cpp @@ -42,10 +42,7 @@ const char* basictype_to_str(BasicType t) { // ------------------------------------------------------------------ // card_table_base -CardTable::CardValue* ci_card_table_address() { - BarrierSet* bs = BarrierSet::barrier_set(); - CardTableBarrierSet* ctbs = barrier_set_cast(bs); - CardTable* ct = ctbs->card_table(); - assert(!UseShenandoahGC, "Shenandoah byte_map_base is not constant."); - return ct->byte_map_base(); +CardTable::CardValue* ci_card_table_address_const() { + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); + return ctbs->card_table_base_const(); } diff --git a/src/hotspot/share/ci/ciUtilities.hpp b/src/hotspot/share/ci/ciUtilities.hpp index 75dbb03adf4..3333972d57d 100644 --- a/src/hotspot/share/ci/ciUtilities.hpp +++ b/src/hotspot/share/ci/ciUtilities.hpp @@ -51,9 +51,9 @@ inline const char* bool_to_str(bool b) { const char* basictype_to_str(BasicType t); -CardTable::CardValue* ci_card_table_address(); +CardTable::CardValue* ci_card_table_address_const(); template T ci_card_table_address_as() { - return reinterpret_cast(ci_card_table_address()); + return reinterpret_cast(ci_card_table_address_const()); } #endif // SHARE_CI_CIUTILITIES_HPP diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index c9d9d3632b5..817d0c64d11 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -943,6 +943,7 @@ public: _java_lang_Deprecated_for_removal, _jdk_internal_vm_annotation_AOTSafeClassInitializer, _method_AOTRuntimeSetup, + _jdk_internal_vm_annotation_TrustFinalFields, _annotation_LIMIT }; const Location _location; @@ -1878,6 +1879,11 @@ AnnotationCollector::annotation_index(const ClassLoaderData* loader_data, if (!privileged) break; // only allow in privileged code return _field_Stable; } + case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_TrustFinalFields_signature): { + if (_location != _in_class) break; // only allow for classes + if (!privileged) break; // only allow in privileged code + return _jdk_internal_vm_annotation_TrustFinalFields; + } case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_Contended_signature): { if (_location != _in_field && _location != _in_class) { break; // only allow for fields and classes @@ -1992,6 +1998,9 @@ void ClassFileParser::ClassAnnotationCollector::apply_to(InstanceKlass* ik) { if (has_annotation(_jdk_internal_vm_annotation_AOTSafeClassInitializer)) { ik->set_has_aot_safe_initializer(); } + if (has_annotation(_jdk_internal_vm_annotation_TrustFinalFields)) { + ik->set_trust_final_fields(true); + } } #define MAX_ARGS_SIZE 255 diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index d9a63cd154b..f631bfaa102 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -1418,6 +1418,10 @@ char* ClassLoader::lookup_vm_options() { jio_snprintf(modules_path, JVM_MAXPATHLEN, "%s%slib%smodules", Arguments::get_java_home(), fileSep, fileSep); JImage_file =(*JImageOpen)(modules_path, &error); if (JImage_file == nullptr) { + if (Arguments::has_jimage()) { + // The modules file exists but is unreadable or corrupt + vm_exit_during_initialization(err_msg("Unable to load %s", modules_path)); + } return nullptr; } diff --git a/src/hotspot/share/classfile/classLoaderDataShared.cpp b/src/hotspot/share/classfile/classLoaderDataShared.cpp index 7a7743edd03..d415fe64bac 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.cpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -33,6 +33,7 @@ #include "classfile/packageEntry.hpp" #include "classfile/systemDictionary.hpp" #include "logging/log.hpp" +#include "memory/metaspaceClosure.hpp" #include "runtime/handles.inline.hpp" #include "runtime/safepoint.hpp" @@ -56,9 +57,9 @@ class ArchivedClassLoaderData { public: ArchivedClassLoaderData() : _packages(nullptr), _modules(nullptr), _unnamed_module(nullptr) {} - void iterate_symbols(ClassLoaderData* loader_data, MetaspaceClosure* closure); - void allocate(ClassLoaderData* loader_data); - void init_archived_entries(ClassLoaderData* loader_data); + void iterate_roots(MetaspaceClosure* closure); + void build_tables(ClassLoaderData* loader_data, TRAPS); + void remove_unshareable_info(); ModuleEntry* unnamed_module() { return _unnamed_module; } @@ -80,17 +81,14 @@ static ModuleEntry* _archived_javabase_moduleEntry = nullptr; static int _platform_loader_root_index = -1; static int _system_loader_root_index = -1; -void ArchivedClassLoaderData::iterate_symbols(ClassLoaderData* loader_data, MetaspaceClosure* closure) { +void ArchivedClassLoaderData::iterate_roots(MetaspaceClosure* it) { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); - assert_valid(loader_data); - if (loader_data != nullptr) { - loader_data->packages()->iterate_symbols(closure); - loader_data->modules() ->iterate_symbols(closure); - loader_data->unnamed_module()->iterate_symbols(closure); - } + it->push(&_packages); + it->push(&_modules); + it->push(&_unnamed_module); } -void ArchivedClassLoaderData::allocate(ClassLoaderData* loader_data) { +void ArchivedClassLoaderData::build_tables(ClassLoaderData* loader_data, TRAPS) { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); assert_valid(loader_data); if (loader_data != nullptr) { @@ -98,19 +96,28 @@ void ArchivedClassLoaderData::allocate(ClassLoaderData* loader_data) { // address of the Symbols, which may be relocated at runtime due to ASLR. // So we store the packages/modules in Arrays. At runtime, we create // the hashtables using these arrays. - _packages = loader_data->packages()->allocate_archived_entries(); - _modules = loader_data->modules() ->allocate_archived_entries(); - _unnamed_module = loader_data->unnamed_module()->allocate_archived_entry(); + _packages = loader_data->packages()->build_aot_table(loader_data, CHECK); + _modules = loader_data->modules()->build_aot_table(loader_data, CHECK); + _unnamed_module = loader_data->unnamed_module(); } } -void ArchivedClassLoaderData::init_archived_entries(ClassLoaderData* loader_data) { - assert(CDSConfig::is_dumping_full_module_graph(), "must be"); - assert_valid(loader_data); - if (loader_data != nullptr) { - loader_data->packages()->init_archived_entries(_packages); - loader_data->modules() ->init_archived_entries(_modules); - _unnamed_module->init_as_archived_entry(); +void ArchivedClassLoaderData::remove_unshareable_info() { + if (_packages != nullptr) { + _packages = ArchiveBuilder::current()->get_buffered_addr(_packages); + for (int i = 0; i < _packages->length(); i++) { + _packages->at(i)->remove_unshareable_info(); + } + } + if (_modules != nullptr) { + _modules = ArchiveBuilder::current()->get_buffered_addr(_modules); + for (int i = 0; i < _modules->length(); i++) { + _modules->at(i)->remove_unshareable_info(); + } + } + if (_unnamed_module != nullptr) { + _unnamed_module = ArchiveBuilder::current()->get_buffered_addr(_unnamed_module); + _unnamed_module->remove_unshareable_info(); } } @@ -153,7 +160,6 @@ void ArchivedClassLoaderData::clear_archived_oops() { // ------------------------------ void ClassLoaderDataShared::load_archived_platform_and_system_class_loaders() { -#if INCLUDE_CDS_JAVA_HEAP // The streaming object loader prefers loading the class loader related objects before // the CLD constructor which has a NoSafepointVerifier. if (!HeapShared::is_loading_streaming_mode()) { @@ -178,7 +184,6 @@ void ClassLoaderDataShared::load_archived_platform_and_system_class_loaders() { if (system_loader_module_entry != nullptr) { system_loader_module_entry->preload_archived_oops(); } -#endif } static ClassLoaderData* null_class_loader_data() { @@ -210,28 +215,27 @@ void ClassLoaderDataShared::ensure_module_entry_table_exists(oop class_loader) { assert(met != nullptr, "sanity"); } -void ClassLoaderDataShared::iterate_symbols(MetaspaceClosure* closure) { +void ClassLoaderDataShared::build_tables(TRAPS) { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); - _archived_boot_loader_data.iterate_symbols (null_class_loader_data(), closure); - _archived_platform_loader_data.iterate_symbols(java_platform_loader_data_or_null(), closure); - _archived_system_loader_data.iterate_symbols (java_system_loader_data_or_null(), closure); + _archived_boot_loader_data.build_tables(null_class_loader_data(), CHECK); + _archived_platform_loader_data.build_tables(java_platform_loader_data_or_null(), CHECK); + _archived_system_loader_data.build_tables(java_system_loader_data_or_null(), CHECK); } -void ClassLoaderDataShared::allocate_archived_tables() { +void ClassLoaderDataShared::iterate_roots(MetaspaceClosure* it) { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); - _archived_boot_loader_data.allocate (null_class_loader_data()); - _archived_platform_loader_data.allocate(java_platform_loader_data_or_null()); - _archived_system_loader_data.allocate (java_system_loader_data_or_null()); + _archived_boot_loader_data.iterate_roots(it); + _archived_platform_loader_data.iterate_roots(it); + _archived_system_loader_data.iterate_roots(it); } -void ClassLoaderDataShared::init_archived_tables() { +void ClassLoaderDataShared::remove_unshareable_info() { assert(CDSConfig::is_dumping_full_module_graph(), "must be"); + _archived_boot_loader_data.remove_unshareable_info(); + _archived_platform_loader_data.remove_unshareable_info(); + _archived_system_loader_data.remove_unshareable_info(); - _archived_boot_loader_data.init_archived_entries (null_class_loader_data()); - _archived_platform_loader_data.init_archived_entries(java_platform_loader_data_or_null()); - _archived_system_loader_data.init_archived_entries (java_system_loader_data_or_null()); - - _archived_javabase_moduleEntry = ModuleEntry::get_archived_entry(ModuleEntryTable::javabase_moduleEntry()); + _archived_javabase_moduleEntry = ArchiveBuilder::current()->get_buffered_addr(ModuleEntryTable::javabase_moduleEntry()); _platform_loader_root_index = HeapShared::append_root(SystemDictionary::java_platform_loader()); _system_loader_root_index = HeapShared::append_root(SystemDictionary::java_system_loader()); @@ -271,7 +275,6 @@ ModuleEntry* ClassLoaderDataShared::archived_unnamed_module(ClassLoaderData* loa return archived_module; } - void ClassLoaderDataShared::clear_archived_oops() { assert(!CDSConfig::is_using_full_module_graph(), "must be"); _archived_boot_loader_data.clear_archived_oops(); diff --git a/src/hotspot/share/classfile/classLoaderDataShared.hpp b/src/hotspot/share/classfile/classLoaderDataShared.hpp index 39d0a89418f..2cf37310e50 100644 --- a/src/hotspot/share/classfile/classLoaderDataShared.hpp +++ b/src/hotspot/share/classfile/classLoaderDataShared.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -40,11 +40,11 @@ class ClassLoaderDataShared : AllStatic { public: static void load_archived_platform_and_system_class_loaders() NOT_CDS_JAVA_HEAP_RETURN; static void restore_archived_modules_for_preloading_classes(JavaThread* current) NOT_CDS_JAVA_HEAP_RETURN; + static void build_tables(TRAPS) NOT_CDS_JAVA_HEAP_RETURN; + static void iterate_roots(MetaspaceClosure* closure) NOT_CDS_JAVA_HEAP_RETURN; + static void remove_unshareable_info() NOT_CDS_JAVA_HEAP_RETURN; #if INCLUDE_CDS_JAVA_HEAP static void ensure_module_entry_tables_exist(); - static void allocate_archived_tables(); - static void iterate_symbols(MetaspaceClosure* closure); - static void init_archived_tables(); static void serialize(SerializeClosure* f); static void clear_archived_oops(); static void restore_archived_entries_for_null_class_loader_data(); diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index dd70d7b49ab..b650bf8cfb8 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1263,6 +1263,10 @@ bool java_lang_Class::restore_archived_mirror(Klass *k, "Restored %s archived mirror " PTR_FORMAT, k->external_name(), p2i(mirror())); } + if (CDSConfig::is_dumping_heap()) { + create_scratch_mirror(k, CHECK_(false)); + } + return true; } #endif // INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp index 5fb3d6f2d13..b5b8aa4ef55 100644 --- a/src/hotspot/share/classfile/moduleEntry.cpp +++ b/src/hotspot/share/classfile/moduleEntry.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -23,6 +23,7 @@ */ #include "cds/aotClassLocation.hpp" +#include "cds/aotGrowableArray.inline.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" @@ -37,6 +38,7 @@ #include "jni.h" #include "logging/log.hpp" #include "logging/logStream.hpp" +#include "memory/metadataFactory.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/oopHandle.inline.hpp" @@ -44,7 +46,6 @@ #include "runtime/handles.inline.hpp" #include "runtime/safepoint.hpp" #include "utilities/events.hpp" -#include "utilities/growableArray.hpp" #include "utilities/hashTable.hpp" #include "utilities/ostream.hpp" #include "utilities/quickSort.hpp" @@ -167,7 +168,7 @@ void ModuleEntry::add_read(ModuleEntry* m) { } else { if (reads() == nullptr) { // Lazily create a module's reads list - GrowableArray* new_reads = new (mtModule) GrowableArray(MODULE_READS_SIZE, mtModule); + AOTGrowableArray* new_reads = new (mtModule) AOTGrowableArray(MODULE_READS_SIZE, mtModule); set_reads(new_reads); } @@ -274,8 +275,7 @@ ModuleEntry::ModuleEntry(Handle module_handle, _has_default_read_edges(false), _must_walk_reads(false), _is_open(is_open), - _is_patched(false) - DEBUG_ONLY(COMMA _reads_is_archived(false)) { + _is_patched(false) { // Initialize fields specific to a ModuleEntry if (_name == nullptr) { @@ -394,7 +394,6 @@ ModuleEntryTable::~ModuleEntryTable() { ModuleEntryTableDeleter deleter; _table.unlink(&deleter); assert(_table.number_of_entries() == 0, "should have removed all entries"); - } void ModuleEntry::set_loader_data(ClassLoaderData* cld) { @@ -402,147 +401,51 @@ void ModuleEntry::set_loader_data(ClassLoaderData* cld) { _loader_data = cld; } +void ModuleEntry::metaspace_pointers_do(MetaspaceClosure* it) { + it->push(&_name); + it->push(&_reads); + it->push(&_version); + it->push(&_location); +} + #if INCLUDE_CDS_JAVA_HEAP -typedef HashTable< - const ModuleEntry*, - ModuleEntry*, - 557, // prime number - AnyObj::C_HEAP> ArchivedModuleEntries; -static ArchivedModuleEntries* _archive_modules_entries = nullptr; - -#ifndef PRODUCT -static int _num_archived_module_entries = 0; -static int _num_inited_module_entries = 0; -#endif - bool ModuleEntry::should_be_archived() const { return SystemDictionaryShared::is_builtin_loader(loader_data()); } -ModuleEntry* ModuleEntry::allocate_archived_entry() const { - precond(should_be_archived()); - precond(CDSConfig::is_dumping_full_module_graph()); - ModuleEntry* archived_entry = (ModuleEntry*)ArchiveBuilder::rw_region_alloc(sizeof(ModuleEntry)); - memcpy((void*)archived_entry, (void*)this, sizeof(ModuleEntry)); +void ModuleEntry::remove_unshareable_info() { + _archived_module_index = HeapShared::append_root(module_oop()); - archived_entry->_archived_module_index = HeapShared::append_root(module_oop()); - if (_archive_modules_entries == nullptr) { - _archive_modules_entries = new (mtClass)ArchivedModuleEntries(); - } - assert(_archive_modules_entries->get(this) == nullptr, "Each ModuleEntry must not be shared across ModuleEntryTables"); - _archive_modules_entries->put(this, archived_entry); - DEBUG_ONLY(_num_archived_module_entries++); - - if (CDSConfig::is_dumping_final_static_archive()) { - OopHandle null_handle; - archived_entry->_shared_pd = null_handle; - } else { - assert(archived_entry->shared_protection_domain() == nullptr, "never set during -Xshare:dump"); + if (_reads != nullptr) { + _reads->set_in_aot_cache(); } // Clear handles and restore at run time. Handles cannot be archived. + if (CDSConfig::is_dumping_final_static_archive()) { + OopHandle null_handle; + _shared_pd = null_handle; + } else { + assert(shared_protection_domain() == nullptr, "never set during -Xshare:dump"); + } + OopHandle null_handle; - archived_entry->_module_handle = null_handle; - - // For verify_archived_module_entries() - DEBUG_ONLY(_num_inited_module_entries++); - - if (log_is_enabled(Info, aot, module)) { - ResourceMark rm; - LogStream ls(Log(aot, module)::info()); - ls.print("Stored in archive: "); - archived_entry->print(&ls); - } - return archived_entry; -} - -bool ModuleEntry::has_been_archived() { - assert(!ArchiveBuilder::current()->is_in_buffer_space(this), "must be called on original ModuleEntry"); - return _archive_modules_entries->contains(this); -} - -ModuleEntry* ModuleEntry::get_archived_entry(ModuleEntry* orig_entry) { - ModuleEntry** ptr = _archive_modules_entries->get(orig_entry); - assert(ptr != nullptr && *ptr != nullptr, "must have been allocated"); - return *ptr; -} - -// This function is used to archive ModuleEntry::_reads and PackageEntry::_qualified_exports. -// GrowableArray cannot be directly archived, as it needs to be expandable at runtime. -// Write it out as an Array, and convert it back to GrowableArray at runtime. -Array* ModuleEntry::write_growable_array(GrowableArray* array) { - Array* archived_array = nullptr; - int length = (array == nullptr) ? 0 : array->length(); - if (length > 0) { - archived_array = ArchiveBuilder::new_ro_array(length); - for (int i = 0; i < length; i++) { - ModuleEntry* archived_entry = get_archived_entry(array->at(i)); - archived_array->at_put(i, archived_entry); - ArchivePtrMarker::mark_pointer((address*)archived_array->adr_at(i)); - } - } - - return archived_array; -} - -GrowableArray* ModuleEntry::restore_growable_array(Array* archived_array) { - GrowableArray* array = nullptr; - int length = (archived_array == nullptr) ? 0 : archived_array->length(); - if (length > 0) { - array = new (mtModule) GrowableArray(length, mtModule); - for (int i = 0; i < length; i++) { - ModuleEntry* archived_entry = archived_array->at(i); - array->append(archived_entry); - } - } - - return array; -} - -void ModuleEntry::iterate_symbols(MetaspaceClosure* closure) { - closure->push(&_name); - closure->push(&_version); - closure->push(&_location); -} - -void ModuleEntry::init_as_archived_entry() { - set_archived_reads(write_growable_array(reads())); + _module_handle = null_handle; _loader_data = nullptr; // re-init at runtime if (name() != nullptr) { - _shared_path_index = AOTClassLocationConfig::dumptime()->get_module_shared_path_index(_location); - _name = ArchiveBuilder::get_buffered_symbol(_name); - ArchivePtrMarker::mark_pointer((address*)&_name); + Symbol* src_location = ArchiveBuilder::current()->get_source_addr(_location); + _shared_path_index = AOTClassLocationConfig::dumptime()->get_module_shared_path_index(src_location); } else { // _shared_path_index is used only by SystemDictionary::is_shared_class_visible_impl() // for checking classes in named modules. _shared_path_index = -1; } - if (_version != nullptr) { - _version = ArchiveBuilder::get_buffered_symbol(_version); - } - if (_location != nullptr) { - _location = ArchiveBuilder::get_buffered_symbol(_location); - } JFR_ONLY(set_trace_id(0);) // re-init at runtime - - ArchivePtrMarker::mark_pointer((address*)&_reads); - ArchivePtrMarker::mark_pointer((address*)&_version); - ArchivePtrMarker::mark_pointer((address*)&_location); } -#ifndef PRODUCT -void ModuleEntry::verify_archived_module_entries() { - assert(_num_archived_module_entries == _num_inited_module_entries, - "%d ModuleEntries have been archived but %d of them have been properly initialized with archived java.lang.Module objects", - _num_archived_module_entries, _num_inited_module_entries); -} -#endif // PRODUCT - void ModuleEntry::load_from_archive(ClassLoaderData* loader_data) { assert(CDSConfig::is_using_archive(), "runtime only"); set_loader_data(loader_data); - set_reads(restore_growable_array(archived_reads())); JFR_ONLY(INIT_ID(this);) } @@ -581,38 +484,28 @@ static int compare_module_by_name(ModuleEntry* a, ModuleEntry* b) { return a->name()->fast_compare(b->name()); } -void ModuleEntryTable::iterate_symbols(MetaspaceClosure* closure) { - auto syms = [&] (const SymbolHandle& key, ModuleEntry*& m) { - m->iterate_symbols(closure); - }; - _table.iterate_all(syms); -} - -Array* ModuleEntryTable::allocate_archived_entries() { - Array* archived_modules = ArchiveBuilder::new_rw_array(_table.number_of_entries()); +Array* ModuleEntryTable::build_aot_table(ClassLoaderData* loader_data, TRAPS) { + Array* aot_table = + MetadataFactory::new_array(loader_data, _table.number_of_entries(), nullptr, CHECK_NULL); int n = 0; auto grab = [&] (const SymbolHandle& key, ModuleEntry*& m) { - archived_modules->at_put(n++, m); + m->pack_reads(); + aot_table->at_put(n++, m); + if (log_is_enabled(Info, aot, module)) { + ResourceMark rm; + LogStream ls(Log(aot, module)::info()); + ls.print("Stored in archive: "); + m->print(&ls); + } }; _table.iterate_all(grab); if (n > 1) { // Always allocate in the same order to produce deterministic archive. - QuickSort::sort(archived_modules->data(), n, compare_module_by_name); + QuickSort::sort(aot_table->data(), n, compare_module_by_name); } - for (int i = 0; i < n; i++) { - archived_modules->at_put(i, archived_modules->at(i)->allocate_archived_entry()); - ArchivePtrMarker::mark_pointer((address*)archived_modules->adr_at(i)); - } - return archived_modules; -} -void ModuleEntryTable::init_archived_entries(Array* archived_modules) { - assert(CDSConfig::is_dumping_full_module_graph(), "sanity"); - for (int i = 0; i < archived_modules->length(); i++) { - ModuleEntry* archived_entry = archived_modules->at(i); - archived_entry->init_as_archived_entry(); - } + return aot_table; } void ModuleEntryTable::load_archived_entries(ClassLoaderData* loader_data, diff --git a/src/hotspot/share/classfile/moduleEntry.hpp b/src/hotspot/share/classfile/moduleEntry.hpp index 2e1852c5369..1a0251a2c2a 100644 --- a/src/hotspot/share/classfile/moduleEntry.hpp +++ b/src/hotspot/share/classfile/moduleEntry.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -25,7 +25,9 @@ #ifndef SHARE_CLASSFILE_MODULEENTRY_HPP #define SHARE_CLASSFILE_MODULEENTRY_HPP +#include "cds/aotGrowableArray.hpp" #include "jni.h" +#include "memory/metaspaceClosureType.hpp" #include "oops/oopHandle.hpp" #include "oops/symbol.hpp" #include "oops/symbolHandle.hpp" @@ -68,11 +70,8 @@ private: // for shared classes from this module Symbol* _name; // name of this module ClassLoaderData* _loader_data; + AOTGrowableArray* _reads; // list of modules that are readable by this module - union { - GrowableArray* _reads; // list of modules that are readable by this module - Array* _archived_reads; // List of readable modules stored in the CDS archive - }; Symbol* _version; // module version number Symbol* _location; // module location CDS_ONLY(int _shared_path_index;) // >=0 if classes in this module are in CDS archive @@ -81,7 +80,6 @@ private: bool _must_walk_reads; // walk module's reads list at GC safepoints to purge out dead modules bool _is_open; // whether the packages in the module are all unqualifiedly exported bool _is_patched; // whether the module is patched via --patch-module - DEBUG_ONLY(bool _reads_is_archived); CDS_JAVA_HEAP_ONLY(int _archived_module_index;) JFR_ONLY(DEFINE_TRACE_ID_FIELD;) @@ -120,22 +118,18 @@ public: bool can_read(ModuleEntry* m) const; bool has_reads_list() const; - GrowableArray* reads() const { - assert(!_reads_is_archived, "sanity"); + AOTGrowableArray* reads() const { return _reads; } - void set_reads(GrowableArray* r) { + void set_reads(AOTGrowableArray* r) { _reads = r; - DEBUG_ONLY(_reads_is_archived = false); } - Array* archived_reads() const { - assert(_reads_is_archived, "sanity"); - return _archived_reads; - } - void set_archived_reads(Array* r) { - _archived_reads = r; - DEBUG_ONLY(_reads_is_archived = true); + void pack_reads() { + if (_reads != nullptr) { + _reads->shrink_to_fit(); + } } + void add_read(ModuleEntry* m); void set_read_walk_required(ClassLoaderData* m_loader_data); @@ -189,6 +183,13 @@ public: const char* name_as_C_string() const { return is_named() ? name()->as_C_string() : UNNAMED_MODULE; } + + // methods required by MetaspaceClosure + void metaspace_pointers_do(MetaspaceClosure* it); + int size_in_heapwords() const { return (int)heap_word_size(sizeof(ModuleEntry)); } + MetaspaceClosureType type() const { return MetaspaceClosureType::ModuleEntryType; } + static bool is_read_only_by_default() { return false; } + void print(outputStream* st = tty) const; void verify(); @@ -198,18 +199,11 @@ public: #if INCLUDE_CDS_JAVA_HEAP bool should_be_archived() const; - void iterate_symbols(MetaspaceClosure* closure); - ModuleEntry* allocate_archived_entry() const; - void init_as_archived_entry(); - static ModuleEntry* get_archived_entry(ModuleEntry* orig_entry); - bool has_been_archived(); - static Array* write_growable_array(GrowableArray* array); - static GrowableArray* restore_growable_array(Array* archived_array); + void remove_unshareable_info(); void load_from_archive(ClassLoaderData* loader_data); void preload_archived_oops(); void restore_archived_oops(ClassLoaderData* loader_data); void clear_archived_oops(); - static void verify_archived_module_entries() PRODUCT_RETURN; #endif }; @@ -275,9 +269,7 @@ public: void verify(); #if INCLUDE_CDS_JAVA_HEAP - void iterate_symbols(MetaspaceClosure* closure); - Array* allocate_archived_entries(); - void init_archived_entries(Array* archived_modules); + Array* build_aot_table(ClassLoaderData* loader_data, TRAPS); void load_archived_entries(ClassLoaderData* loader_data, Array* archived_modules); void restore_archived_oops(ClassLoaderData* loader_data, diff --git a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp index baf2acfb78c..51d09d9c47f 100644 --- a/src/hotspot/share/classfile/modules.cpp +++ b/src/hotspot/share/classfile/modules.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2016, 2026, 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 @@ -505,13 +505,10 @@ void Modules::check_archived_module_oop(oop orig_module_obj) { ClassLoaderData* loader_data = orig_module_ent->loader_data(); assert(loader_data->is_builtin_class_loader_data(), "must be"); - if (orig_module_ent->name() != nullptr) { - // For each named module, we archive both the java.lang.Module oop and the ModuleEntry. - assert(orig_module_ent->has_been_archived(), "sanity"); - } else { + precond(ArchiveBuilder::current()->has_been_archived(orig_module_ent)); + if (orig_module_ent->name() == nullptr) { // We always archive unnamed module oop for boot, platform, and system loaders. precond(orig_module_ent->should_be_archived()); - precond(orig_module_ent->has_been_archived()); if (loader_data->is_boot_class_loader_data()) { assert(!_seen_boot_unnamed_module, "only once"); @@ -529,10 +526,6 @@ void Modules::check_archived_module_oop(oop orig_module_obj) { } } -void Modules::verify_archived_modules() { - ModuleEntry::verify_archived_module_entries(); -} - class Modules::ArchivedProperty { const char* _prop; const bool _numbered; diff --git a/src/hotspot/share/classfile/modules.hpp b/src/hotspot/share/classfile/modules.hpp index 27a22c1017a..75857c8960c 100644 --- a/src/hotspot/share/classfile/modules.hpp +++ b/src/hotspot/share/classfile/modules.hpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2016, 2026, 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 @@ -59,7 +59,6 @@ public: TRAPS) NOT_CDS_JAVA_HEAP_RETURN; static void init_archived_modules(JavaThread* current, Handle h_platform_loader, Handle h_system_loader) NOT_CDS_JAVA_HEAP_RETURN; - static void verify_archived_modules() NOT_CDS_JAVA_HEAP_RETURN; static void dump_archived_module_info() NOT_CDS_JAVA_HEAP_RETURN; static void serialize_archived_module_info(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; diff --git a/src/hotspot/share/classfile/packageEntry.cpp b/src/hotspot/share/classfile/packageEntry.cpp index ea2e6cd1def..3e61f2e3a3e 100644 --- a/src/hotspot/share/classfile/packageEntry.cpp +++ b/src/hotspot/share/classfile/packageEntry.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -22,6 +22,8 @@ * */ +#include "cds/aotGrowableArray.inline.hpp" +#include "cds/aotMetaspace.hpp" #include "cds/archiveBuilder.hpp" #include "cds/archiveUtils.hpp" #include "cds/cdsConfig.hpp" @@ -31,13 +33,13 @@ #include "classfile/vmSymbols.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" +#include "memory/metadataFactory.hpp" #include "memory/resourceArea.hpp" #include "oops/array.hpp" #include "oops/symbol.hpp" #include "runtime/handles.inline.hpp" #include "runtime/java.hpp" #include "utilities/events.hpp" -#include "utilities/growableArray.hpp" #include "utilities/hashTable.hpp" #include "utilities/ostream.hpp" #include "utilities/quickSort.hpp" @@ -51,7 +53,7 @@ PackageEntry::PackageEntry(Symbol* name, ModuleEntry* module) : _qualified_exports(nullptr), _defined_by_cds_in_class_path(0) { - // name can't be null + // name can't be null -- a class in the default package gets a PackageEntry of nullptr. _name->increment_refcount(); JFR_ONLY(INIT_ID(this);) @@ -81,7 +83,7 @@ void PackageEntry::add_qexport(ModuleEntry* m) { if (!has_qual_exports_list()) { // Lazily create a package's qualified exports list. // Initial size is small, do not anticipate export lists to be large. - _qualified_exports = new (mtModule) GrowableArray(QUAL_EXP_SIZE, mtModule); + _qualified_exports = new (mtModule) AOTGrowableArray(QUAL_EXP_SIZE, mtModule); } // Determine, based on this newly established export to module m, @@ -183,12 +185,24 @@ void PackageEntry::purge_qualified_exports() { } void PackageEntry::delete_qualified_exports() { - if (_qualified_exports != nullptr) { + if (_qualified_exports != nullptr && !AOTMetaspace::in_aot_cache(_qualified_exports)) { delete _qualified_exports; } _qualified_exports = nullptr; } +void PackageEntry::pack_qualified_exports() { + if (_qualified_exports != nullptr) { + _qualified_exports->shrink_to_fit(); + } +} + +void PackageEntry::metaspace_pointers_do(MetaspaceClosure* it) { + it->push(&_name); + it->push(&_module); + it->push(&_qualified_exports); +} + PackageEntryTable::PackageEntryTable() { } PackageEntryTable::~PackageEntryTable() { @@ -212,66 +226,19 @@ PackageEntryTable::~PackageEntryTable() { } #if INCLUDE_CDS_JAVA_HEAP -typedef HashTable< - const PackageEntry*, - PackageEntry*, - 557, // prime number - AnyObj::C_HEAP> ArchivedPackageEntries; -static ArchivedPackageEntries* _archived_packages_entries = nullptr; - bool PackageEntry::should_be_archived() const { return module()->should_be_archived(); } -PackageEntry* PackageEntry::allocate_archived_entry() const { - precond(should_be_archived()); - PackageEntry* archived_entry = (PackageEntry*)ArchiveBuilder::rw_region_alloc(sizeof(PackageEntry)); - memcpy((void*)archived_entry, (void*)this, sizeof(PackageEntry)); - - if (_archived_packages_entries == nullptr) { - _archived_packages_entries = new (mtClass)ArchivedPackageEntries(); +void PackageEntry::remove_unshareable_info() { + if (_qualified_exports != nullptr) { + _qualified_exports->set_in_aot_cache(); } - assert(_archived_packages_entries->get(this) == nullptr, "Each PackageEntry must not be shared across PackageEntryTables"); - _archived_packages_entries->put(this, archived_entry); - - return archived_entry; -} - -PackageEntry* PackageEntry::get_archived_entry(PackageEntry* orig_entry) { - PackageEntry** ptr = _archived_packages_entries->get(orig_entry); - if (ptr != nullptr) { - return *ptr; - } else { - return nullptr; - } -} - -void PackageEntry::iterate_symbols(MetaspaceClosure* closure) { - closure->push(&_name); -} - -void PackageEntry::init_as_archived_entry() { - Array* archived_qualified_exports = ModuleEntry::write_growable_array(_qualified_exports); - - _name = ArchiveBuilder::get_buffered_symbol(_name); - _module = ModuleEntry::get_archived_entry(_module); - _qualified_exports = (GrowableArray*)archived_qualified_exports; _defined_by_cds_in_class_path = 0; JFR_ONLY(set_trace_id(0);) // re-init at runtime - - ArchivePtrMarker::mark_pointer((address*)&_name); - ArchivePtrMarker::mark_pointer((address*)&_module); - ArchivePtrMarker::mark_pointer((address*)&_qualified_exports); - - LogStreamHandle(Info, aot, package) st; - if (st.is_enabled()) { - st.print("archived "); - print(&st); - } } void PackageEntry::load_from_archive() { - _qualified_exports = ModuleEntry::restore_growable_array((Array*)_qualified_exports); JFR_ONLY(INIT_ID(this);) } @@ -280,14 +247,7 @@ static int compare_package_by_name(PackageEntry* a, PackageEntry* b) { return a->name()->fast_compare(b->name()); } -void PackageEntryTable::iterate_symbols(MetaspaceClosure* closure) { - auto syms = [&] (const SymbolHandle& key, PackageEntry*& p) { - p->iterate_symbols(closure); - }; - _table.iterate_all(syms); -} - -Array* PackageEntryTable::allocate_archived_entries() { +Array* PackageEntryTable::build_aot_table(ClassLoaderData* loader_data, TRAPS) { // First count the packages in named modules int n = 0; auto count = [&] (const SymbolHandle& key, PackageEntry*& p) { @@ -297,12 +257,19 @@ Array* PackageEntryTable::allocate_archived_entries() { }; _table.iterate_all(count); - Array* archived_packages = ArchiveBuilder::new_rw_array(n); + Array* archived_packages = MetadataFactory::new_array(loader_data, n, nullptr, CHECK_NULL); // reset n n = 0; auto grab = [&] (const SymbolHandle& key, PackageEntry*& p) { if (p->should_be_archived()) { + p->pack_qualified_exports(); archived_packages->at_put(n++, p); + + LogStreamHandle(Info, aot, package) st; + if (st.is_enabled()) { + st.print("archived "); + p->print(&st); + } } }; _table.iterate_all(grab); @@ -311,18 +278,8 @@ Array* PackageEntryTable::allocate_archived_entries() { // Always allocate in the same order to produce deterministic archive. QuickSort::sort(archived_packages->data(), n, compare_package_by_name); } - for (int i = 0; i < n; i++) { - archived_packages->at_put(i, archived_packages->at(i)->allocate_archived_entry()); - ArchivePtrMarker::mark_pointer((address*)archived_packages->adr_at(i)); - } - return archived_packages; -} -void PackageEntryTable::init_archived_entries(Array* archived_packages) { - for (int i = 0; i < archived_packages->length(); i++) { - PackageEntry* archived_entry = archived_packages->at(i); - archived_entry->init_as_archived_entry(); - } + return archived_packages; } void PackageEntryTable::load_archived_entries(Array* archived_packages) { diff --git a/src/hotspot/share/classfile/packageEntry.hpp b/src/hotspot/share/classfile/packageEntry.hpp index 6abf89dc60f..7b174a92287 100644 --- a/src/hotspot/share/classfile/packageEntry.hpp +++ b/src/hotspot/share/classfile/packageEntry.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -25,7 +25,9 @@ #ifndef SHARE_CLASSFILE_PACKAGEENTRY_HPP #define SHARE_CLASSFILE_PACKAGEENTRY_HPP +#include "cds/aotGrowableArray.hpp" #include "classfile/moduleEntry.hpp" +#include "memory/metaspaceClosureType.hpp" #include "oops/symbol.hpp" #include "oops/symbolHandle.hpp" #include "runtime/atomicAccess.hpp" @@ -114,7 +116,7 @@ private: bool _must_walk_exports; // Contains list of modules this package is qualifiedly exported to. Access // to this list is protected by the Module_lock. - GrowableArray* _qualified_exports; + AOTGrowableArray* _qualified_exports; JFR_ONLY(DEFINE_TRACE_ID_FIELD;) // Initial size of a package entry's list of qualified exports. @@ -205,14 +207,24 @@ public: void purge_qualified_exports(); void delete_qualified_exports(); + void pack_qualified_exports(); // used by AOT + + // methods required by MetaspaceClosure + void metaspace_pointers_do(MetaspaceClosure* it); + int size_in_heapwords() const { return (int)heap_word_size(sizeof(PackageEntry)); } + MetaspaceClosureType type() const { return MetaspaceClosureType::PackageEntryType; } + static bool is_read_only_by_default() { return false; } + void print(outputStream* st = tty); + char* name_as_C_string() const { + assert(_name != nullptr, "name can't be null"); + return name()->as_C_string(); + } + #if INCLUDE_CDS_JAVA_HEAP bool should_be_archived() const; - void iterate_symbols(MetaspaceClosure* closure); - PackageEntry* allocate_archived_entry() const; - void init_as_archived_entry(); - static PackageEntry* get_archived_entry(PackageEntry* orig_entry); + void remove_unshareable_info(); void load_from_archive(); #endif @@ -271,9 +283,7 @@ public: void print(outputStream* st = tty); #if INCLUDE_CDS_JAVA_HEAP - void iterate_symbols(MetaspaceClosure* closure); - Array* allocate_archived_entries(); - void init_archived_entries(Array* archived_packages); + Array* build_aot_table(ClassLoaderData* loader_data, TRAPS); void load_archived_entries(Array* archived_packages); #endif }; diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp index 20dfad0d980..2b8b7780a41 100644 --- a/src/hotspot/share/classfile/stringTable.cpp +++ b/src/hotspot/share/classfile/stringTable.cpp @@ -74,24 +74,9 @@ const size_t REHASH_LEN = 100; const double CLEAN_DEAD_HIGH_WATER_MARK = 0.5; #if INCLUDE_CDS_JAVA_HEAP -bool StringTable::_is_two_dimensional_shared_strings_array = false; -OopHandle StringTable::_shared_strings_array; -int StringTable::_shared_strings_array_root_index; - inline oop StringTable::read_string_from_compact_hashtable(address base_address, u4 index) { assert(AOTMappedHeapLoader::is_in_use(), "sanity"); - objArrayOop array = (objArrayOop)(_shared_strings_array.resolve()); - oop s; - - if (!_is_two_dimensional_shared_strings_array) { - s = array->obj_at((int)index); - } else { - int primary_index = index >> _secondary_array_index_bits; - int secondary_index = index & _secondary_array_index_mask; - objArrayOop secondary = (objArrayOop)array->obj_at(primary_index); - s = secondary->obj_at(secondary_index); - } - + oop s = HeapShared::get_root((int)index, false); assert(java_lang_String::is_instance(s), "must be"); return s; } @@ -115,7 +100,6 @@ OopStorage* StringTable::_oop_storage; static size_t _current_size = 0; static volatile size_t _items_count = 0; -DEBUG_ONLY(static bool _disable_interning_during_cds_dump = false); volatile bool _alt_hash = false; @@ -317,12 +301,6 @@ void StringTable::create_table() { _oop_storage->register_num_dead_callback(&gc_notification); } -#if INCLUDE_CDS_JAVA_HEAP -void StringTable::load_shared_strings_array() { - _shared_strings_array = OopHandle(Universe::vm_global(), HeapShared::get_root(_shared_strings_array_root_index)); -} -#endif - void StringTable::item_added() { AtomicAccess::inc(&_items_count); } @@ -509,9 +487,6 @@ oop StringTable::intern(const char* utf8_string, TRAPS) { } oop StringTable::intern(const StringWrapper& name, TRAPS) { - assert(!AtomicAccess::load_acquire(&_disable_interning_during_cds_dump), - "All threads that may intern strings should have been stopped before CDS starts copying the interned string table"); - // shared table always uses java_lang_String::hash_code unsigned int hash = hash_wrapped_string(name); oop found_string = lookup_shared(name, hash); @@ -957,118 +932,13 @@ oop StringTable::lookup_shared(const jchar* name, int len) { return _shared_table.lookup(wrapped_name, java_lang_String::hash_code(name, len), 0); } -// This is called BEFORE we enter the CDS safepoint. We can still allocate Java object arrays to -// be used by the shared strings table. -void StringTable::allocate_shared_strings_array(TRAPS) { - if (!CDSConfig::is_dumping_heap()) { - return; - } +void StringTable::init_shared_table() { + assert(SafepointSynchronize::is_at_safepoint(), "inside AOT safepoint"); + precond(CDSConfig::is_dumping_heap()); + assert(HeapShared::is_writing_mapping_mode(), "not used for streamed oops"); - assert(HeapShared::is_writing_mapping_mode(), "should not reach here"); - - CompileBroker::wait_for_no_active_tasks(); - - precond(CDSConfig::allow_only_single_java_thread()); - - // At this point, no more strings will be added: - // - There's only a single Java thread (this thread). It no longer executes Java bytecodes - // so JIT compilation will eventually stop. - // - CompileBroker has no more active tasks, so all JIT requests have been processed. - - // This flag will be cleared after intern table dumping has completed, so we can run the - // compiler again (for future AOT method compilation, etc). - DEBUG_ONLY(AtomicAccess::release_store(&_disable_interning_during_cds_dump, true)); - - if (items_count_acquire() > (size_t)max_jint) { - fatal("Too many strings to be archived: %zu", items_count_acquire()); - } - - int total = (int)items_count_acquire(); - size_t single_array_size = objArrayOopDesc::object_size(total); - - log_info(aot)("allocated string table for %d strings", total); - - if (!HeapShared::is_too_large_to_archive(single_array_size)) { - // The entire table can fit in a single array - objArrayOop array = oopFactory::new_objArray(vmClasses::Object_klass(), total, CHECK); - _shared_strings_array = OopHandle(Universe::vm_global(), array); - log_info(aot)("string table array (single level) length = %d", total); - } else { - // Split the table in two levels of arrays. - int primary_array_length = (total + _secondary_array_max_length - 1) / _secondary_array_max_length; - size_t primary_array_size = objArrayOopDesc::object_size(primary_array_length); - size_t secondary_array_size = objArrayOopDesc::object_size(_secondary_array_max_length); - - if (HeapShared::is_too_large_to_archive(secondary_array_size)) { - // This can only happen if you have an extremely large number of classes that - // refer to more than 16384 * 16384 = 26M interned strings! Not a practical concern - // but bail out for safety. - log_error(aot)("Too many strings to be archived: %zu", items_count_acquire()); - AOTMetaspace::unrecoverable_writing_error(); - } - - objArrayOop primary = oopFactory::new_objArray(vmClasses::Object_klass(), primary_array_length, CHECK); - objArrayHandle primaryHandle(THREAD, primary); - _shared_strings_array = OopHandle(Universe::vm_global(), primary); - - log_info(aot)("string table array (primary) length = %d", primary_array_length); - for (int i = 0; i < primary_array_length; i++) { - int len; - if (total > _secondary_array_max_length) { - len = _secondary_array_max_length; - } else { - len = total; - } - total -= len; - - objArrayOop secondary = oopFactory::new_objArray(vmClasses::Object_klass(), len, CHECK); - primaryHandle()->obj_at_put(i, secondary); - - log_info(aot)("string table array (secondary)[%d] length = %d", i, len); - assert(!HeapShared::is_too_large_to_archive(secondary), "sanity"); - } - - assert(total == 0, "must be"); - _is_two_dimensional_shared_strings_array = true; - } -} - -#ifndef PRODUCT -void StringTable::verify_secondary_array_index_bits() { - assert(HeapShared::is_writing_mapping_mode(), "should not reach here"); - int max; - for (max = 1; ; max++) { - size_t next_size = objArrayOopDesc::object_size(1 << (max + 1)); - if (HeapShared::is_too_large_to_archive(next_size)) { - break; - } - } - // Currently max is 17 for +UseCompressedOops, 16 for -UseCompressedOops. - // When we add support for Shenandoah (which has a smaller mininum region size than G1), - // max will become 15/14. - // - // We use _secondary_array_index_bits==14 as that will be the eventual value, and will - // make testing easier. - assert(_secondary_array_index_bits <= max, - "_secondary_array_index_bits (%d) must be smaller than max possible value (%d)", - _secondary_array_index_bits, max); -} -#endif // PRODUCT - -// This is called AFTER we enter the CDS safepoint. -// -// For each shared string: -// [1] Store it into _shared_strings_array. Encode its position as a 32-bit index. -// [2] Store the index and hashcode into _shared_table. -oop StringTable::init_shared_strings_array() { - assert(CDSConfig::is_dumping_heap(), "must be"); - assert(HeapShared::is_writing_mapping_mode(), "should not reach here"); - objArrayOop array = (objArrayOop)(_shared_strings_array.resolve()); - - verify_secondary_array_index_bits(); - - int index = 0; - auto copy_into_array = [&] (WeakHandle* val) { + int n = 0; + auto copy_into_aot_heap = [&] (WeakHandle* val) { oop string = val->peek(); if (string != nullptr && !HeapShared::is_string_too_large_to_archive(string)) { // If string is too large, don't put it into the string table. @@ -1077,53 +947,34 @@ oop StringTable::init_shared_strings_array() { // - If there's a reference to it, we will report an error inside HeapShared.cpp and // dumping will fail. HeapShared::add_to_dumped_interned_strings(string); - if (!_is_two_dimensional_shared_strings_array) { - assert(index < array->length(), "no strings should have been added"); - array->obj_at_put(index, string); - } else { - int primary_index = index >> _secondary_array_index_bits; - int secondary_index = index & _secondary_array_index_mask; - - assert(primary_index < array->length(), "no strings should have been added"); - objArrayOop secondary = (objArrayOop)array->obj_at(primary_index); - - assert(secondary != nullptr && secondary->is_objArray(), "must be"); - assert(secondary_index < secondary->length(), "no strings should have been added"); - secondary->obj_at_put(secondary_index, string); - } - index ++; } + n++; return true; }; - _local_table->do_safepoint_scan(copy_into_array); - log_info(aot)("Archived %d interned strings", index); - return array; + _local_table->do_safepoint_scan(copy_into_aot_heap); + log_info(aot)("Archived %d interned strings", n); }; void StringTable::write_shared_table() { + assert(SafepointSynchronize::is_at_safepoint(), "inside AOT safepoint"); + precond(CDSConfig::is_dumping_heap()); + assert(HeapShared::is_writing_mapping_mode(), "not used for streamed oops"); + _shared_table.reset(); CompactHashtableWriter writer((int)items_count_acquire(), ArchiveBuilder::string_stats()); - int index = 0; auto copy_into_shared_table = [&] (WeakHandle* val) { oop string = val->peek(); if (string != nullptr && !HeapShared::is_string_too_large_to_archive(string)) { unsigned int hash = java_lang_String::hash_code(string); - writer.add(hash, index); - index ++; + int root_id = HeapShared::append_root(string); + writer.add(hash, root_id); } return true; }; _local_table->do_safepoint_scan(copy_into_shared_table); writer.dump(&_shared_table, "string"); - - DEBUG_ONLY(AtomicAccess::release_store(&_disable_interning_during_cds_dump, false)); -} - -void StringTable::set_shared_strings_array_index(int root_index) { - assert(HeapShared::is_writing_mapping_mode(), "should not reach here"); - _shared_strings_array_root_index = root_index; } void StringTable::serialize_shared_table_header(SerializeClosure* soc) { @@ -1135,8 +986,27 @@ void StringTable::serialize_shared_table_header(SerializeClosure* soc) { } else if (!AOTMappedHeapLoader::is_in_use()) { _shared_table.reset(); } +} - soc->do_bool(&_is_two_dimensional_shared_strings_array); - soc->do_int(&_shared_strings_array_root_index); +void StringTable::move_shared_strings_into_runtime_table() { + precond(CDSConfig::is_dumping_final_static_archive()); + JavaThread* THREAD = JavaThread::current(); + HandleMark hm(THREAD); + + int n = 0; + _shared_table.iterate_all([&](oop string) { + int length = java_lang_String::length(string); + Handle h_string (THREAD, string); + StringWrapper name(h_string, length); + unsigned int hash = hash_wrapped_string(name); + + assert(!_alt_hash, "too early"); + oop interned = do_intern(name, hash, THREAD); + assert(string == interned, "must be"); + n++; + }); + + _shared_table.reset(); + log_info(aot)("Moved %d interned strings to runtime table", n); } #endif //INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/classfile/stringTable.hpp b/src/hotspot/share/classfile/stringTable.hpp index 839e9d9053d..0024a45a2f2 100644 --- a/src/hotspot/share/classfile/stringTable.hpp +++ b/src/hotspot/share/classfile/stringTable.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -109,49 +109,17 @@ public: static bool needs_rehashing() { return _needs_rehashing; } static inline void update_needs_rehash(bool rehash); - // Sharing -#if INCLUDE_CDS_JAVA_HEAP - static inline oop read_string_from_compact_hashtable(address base_address, u4 index); - + // AOT support + static inline oop read_string_from_compact_hashtable(address base_address, u4 index) NOT_CDS_JAVA_HEAP_RETURN_(nullptr); private: - static bool _is_two_dimensional_shared_strings_array; - static OopHandle _shared_strings_array; - static int _shared_strings_array_root_index; - - // All the shared strings are referenced through _shared_strings_array to keep them alive. - // Each shared string is stored as a 32-bit index in ::_shared_table. The index - // is interpreted in two ways: - // - // [1] _is_two_dimensional_shared_strings_array = false: _shared_strings_array is an Object[]. - // Each shared string is stored as _shared_strings_array[index] - // - // [2] _is_two_dimensional_shared_strings_array = true: _shared_strings_array is an Object[][] - // This happens when there are too many elements in the shared table. We store them - // using two levels of objArrays, such that none of the arrays are too big for - // AOTMappedHeapWriter::is_too_large_to_archive(). In this case, the index is splited into two - // parts. Each shared string is stored as _shared_strings_array[primary_index][secondary_index]: - // - // [bits 31 .. 14][ bits 13 .. 0 ] - // primary_index secondary_index - const static int _secondary_array_index_bits = 14; - const static int _secondary_array_max_length = 1 << _secondary_array_index_bits; - const static int _secondary_array_index_mask = _secondary_array_max_length - 1; - - // make sure _secondary_array_index_bits is not too big - static void verify_secondary_array_index_bits() PRODUCT_RETURN; -#endif // INCLUDE_CDS_JAVA_HEAP - - private: static oop lookup_shared(const StringWrapper& name, unsigned int hash) NOT_CDS_JAVA_HEAP_RETURN_(nullptr); - public: +public: static oop lookup_shared(const jchar* name, int len) NOT_CDS_JAVA_HEAP_RETURN_(nullptr); static size_t shared_entry_count() NOT_CDS_JAVA_HEAP_RETURN_(0); - static void allocate_shared_strings_array(TRAPS) NOT_CDS_JAVA_HEAP_RETURN; - static void load_shared_strings_array() NOT_CDS_JAVA_HEAP_RETURN; - static oop init_shared_strings_array() NOT_CDS_JAVA_HEAP_RETURN_(nullptr); + static void init_shared_table() NOT_CDS_JAVA_HEAP_RETURN; static void write_shared_table() NOT_CDS_JAVA_HEAP_RETURN; - static void set_shared_strings_array_index(int root_index) NOT_CDS_JAVA_HEAP_RETURN; static void serialize_shared_table_header(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; + static void move_shared_strings_into_runtime_table(); // Jcmd static void dump(outputStream* st, bool verbose=false); diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 38dba1d3d5f..30f147b9ae7 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -190,9 +190,8 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) { // effect (sic!) for external_name(), but instead of doing that, we opt to // explicitly push the hashcode in here. This is signify the following block // is IMPORTANT: - if (klass->java_mirror() != nullptr) { - klass->java_mirror()->identity_hash(); - } + assert(klass->java_mirror() != nullptr, "must be"); + klass->java_mirror()->identity_hash(); if (!is_eligible_for_verification(klass, should_verify_class)) { return true; diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index 8388b98faae..79646f24d0e 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -245,10 +245,6 @@ class SerializeClosure; \ /* Concurrency support */ \ template(java_util_concurrent_locks_AbstractOwnableSynchronizer, "java/util/concurrent/locks/AbstractOwnableSynchronizer") \ - template(java_util_concurrent_atomic_AtomicIntegerFieldUpdater_Impl, "java/util/concurrent/atomic/AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl") \ - template(java_util_concurrent_atomic_AtomicLongFieldUpdater_CASUpdater, "java/util/concurrent/atomic/AtomicLongFieldUpdater$CASUpdater") \ - template(java_util_concurrent_atomic_AtomicLongFieldUpdater_LockedUpdater, "java/util/concurrent/atomic/AtomicLongFieldUpdater$LockedUpdater") \ - template(java_util_concurrent_atomic_AtomicReferenceFieldUpdater_Impl, "java/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl") \ template(jdk_internal_vm_annotation_Contended_signature, "Ljdk/internal/vm/annotation/Contended;") \ template(jdk_internal_vm_annotation_ReservedStackAccess_signature, "Ljdk/internal/vm/annotation/ReservedStackAccess;") \ template(jdk_internal_ValueBased_signature, "Ljdk/internal/ValueBased;") \ @@ -302,6 +298,7 @@ class SerializeClosure; template(jdk_internal_misc_Scoped_signature, "Ljdk/internal/misc/ScopedMemoryAccess$Scoped;") \ template(jdk_internal_vm_annotation_IntrinsicCandidate_signature, "Ljdk/internal/vm/annotation/IntrinsicCandidate;") \ template(jdk_internal_vm_annotation_Stable_signature, "Ljdk/internal/vm/annotation/Stable;") \ + template(jdk_internal_vm_annotation_TrustFinalFields_signature, "Ljdk/internal/vm/annotation/TrustFinalFields;") \ \ template(jdk_internal_vm_annotation_ChangesCurrentThread_signature, "Ljdk/internal/vm/annotation/ChangesCurrentThread;") \ template(jdk_internal_vm_annotation_JvmtiHideEvents_signature, "Ljdk/internal/vm/annotation/JvmtiHideEvents;") \ diff --git a/src/hotspot/share/code/dependencies.cpp b/src/hotspot/share/code/dependencies.cpp index d90695739a1..dbfe1cd884e 100644 --- a/src/hotspot/share/code/dependencies.cpp +++ b/src/hotspot/share/code/dependencies.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -1124,7 +1124,7 @@ class AbstractClassHierarchyWalker { Klass* find_witness(InstanceKlass* context_type, KlassDepChange* changes = nullptr); static void init(); - static void print_statistics(); + NOT_PRODUCT(static void print_statistics();) }; PerfCounter* AbstractClassHierarchyWalker::_perf_find_witness_anywhere_calls_count = nullptr; @@ -2277,6 +2277,7 @@ bool KlassDepChange::involves_context(Klass* k) { return is_contained; } +#ifndef PRODUCT void Dependencies::print_statistics() { AbstractClassHierarchyWalker::print_statistics(); } @@ -2302,6 +2303,7 @@ void AbstractClassHierarchyWalker::print_statistics() { } } } +#endif CallSiteDepChange::CallSiteDepChange(Handle call_site, Handle method_handle) : _call_site(call_site), diff --git a/src/hotspot/share/code/dependencies.hpp b/src/hotspot/share/code/dependencies.hpp index d11c51a66dc..582a08183f9 100644 --- a/src/hotspot/share/code/dependencies.hpp +++ b/src/hotspot/share/code/dependencies.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -649,7 +649,7 @@ class Dependencies: public ResourceObj { }; friend class Dependencies::DepStream; - static void print_statistics(); + NOT_PRODUCT(static void print_statistics();) }; diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index 574f4d6543b..7b236ed3589 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -2346,12 +2346,18 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { /* Repeat compilation without installing code for profiling purposes */ int repeat_compilation_count = directive->RepeatCompilationOption; - while (repeat_compilation_count > 0) { - ResourceMark rm(thread); - task->print_ul("NO CODE INSTALLED"); - thread->timeout()->reset(); - comp->compile_method(&ci_env, target, osr_bci, false, directive); - repeat_compilation_count--; + if (repeat_compilation_count > 0) { + CHeapStringHolder failure_reason; + failure_reason.set(ci_env._failure_reason.get()); + while (repeat_compilation_count > 0) { + ResourceMark rm(thread); + task->print_ul("NO CODE INSTALLED"); + thread->timeout()->reset(); + ci_env._failure_reason.clear(); + comp->compile_method(&ci_env, target, osr_bci, false, directive); + repeat_compilation_count--; + } + ci_env._failure_reason.set(failure_reason.get()); } } diff --git a/src/hotspot/share/compiler/disassembler.cpp b/src/hotspot/share/compiler/disassembler.cpp index c79c15e0f32..2c1ef235e07 100644 --- a/src/hotspot/share/compiler/disassembler.cpp +++ b/src/hotspot/share/compiler/disassembler.cpp @@ -607,10 +607,27 @@ void decode_env::print_address(address adr) { return; } + address card_table_base = nullptr; BarrierSet* bs = BarrierSet::barrier_set(); - if (bs->is_a(BarrierSet::CardTableBarrierSet) && - adr == ci_card_table_address_as

    ()) { - st->print("word_map_base"); +#if INCLUDE_G1GC + if (bs->is_a(BarrierSet::G1BarrierSet)) { + G1BarrierSet* g1bs = barrier_set_cast(bs); + card_table_base = g1bs->card_table()->byte_map_base(); + } else +#endif +#if INCLUDE_SHENANDOAHGC + if (bs->is_a(BarrierSet::ShenandoahBarrierSet)) { + ShenandoahBarrierSet* sbs = barrier_set_cast(bs); + if (sbs->card_table() != nullptr) { + card_table_base = sbs->card_table()->byte_map_base(); + } + } else +#endif + if (bs->is_a(BarrierSet::CardTableBarrierSet)) { + card_table_base = ci_card_table_address_as
    (); + } + if (card_table_base != nullptr && adr == card_table_base) { + st->print("card_table_base"); if (WizardMode) st->print(" " INTPTR_FORMAT, p2i(adr)); return; } diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.cpp b/src/hotspot/share/gc/g1/g1BarrierSet.cpp index 622651ce0d8..dee50500e07 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.cpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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,13 +64,13 @@ G1BarrierSet::G1BarrierSet(G1CardTable* card_table, {} G1BarrierSet::~G1BarrierSet() { - delete _refinement_table; + delete refinement_table(); } void G1BarrierSet::swap_global_card_table() { - G1CardTable* temp = static_cast(_card_table); - _card_table = _refinement_table; - _refinement_table = temp; + G1CardTable* temp = static_cast(card_table()); + _card_table.store_relaxed(refinement_table()); + _refinement_table.store_relaxed(temp); } void G1BarrierSet::update_card_table_base(Thread* thread) { @@ -80,7 +80,7 @@ void G1BarrierSet::update_card_table_base(Thread* thread) { assert(thread->is_Java_thread(), "may only update card table base of JavaThreads, not %s", thread->name()); } #endif - G1ThreadLocalData::set_byte_map_base(thread, _card_table->byte_map_base()); + G1ThreadLocalData::set_byte_map_base(thread, card_table()->byte_map_base()); } template void @@ -135,10 +135,10 @@ void G1BarrierSet::write_region(MemRegion mr) { // marks next time. // If we write to the old card table (after the switching, then the refinement // table) the oncoming handshake will do the memory synchronization. - CardTable* card_table = AtomicAccess::load(&_card_table); + CardTable* local_card_table = card_table(); - volatile CardValue* byte = card_table->byte_for(mr.start()); - CardValue* last_byte = card_table->byte_for(mr.last()); + volatile CardValue* byte = local_card_table->byte_for(mr.start()); + CardValue* last_byte = local_card_table->byte_for(mr.last()); // Dirty cards only if necessary. for (; byte <= last_byte; byte++) { @@ -190,6 +190,6 @@ void G1BarrierSet::on_thread_detach(Thread* thread) { } void G1BarrierSet::print_on(outputStream* st) const { - _card_table->print_on(st, "Card"); - _refinement_table->print_on(st, "Refinement"); + card_table()->print_on(st, "Card"); + refinement_table()->print_on(st, "Refinement"); } diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.hpp index bf595973a32..406096acf10 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ #include "gc/shared/bufferNode.hpp" #include "gc/shared/cardTable.hpp" #include "gc/shared/cardTableBarrierSet.hpp" +#include "runtime/atomic.hpp" class G1CardTable; class Thread; @@ -66,7 +67,7 @@ class G1BarrierSet: public CardTableBarrierSet { BufferNode::Allocator _satb_mark_queue_buffer_allocator; G1SATBMarkQueueSet _satb_mark_queue_set; - G1CardTable* _refinement_table; + Atomic _refinement_table; public: G1BarrierSet(G1CardTable* card_table, G1CardTable* refinement_table); @@ -76,7 +77,7 @@ class G1BarrierSet: public CardTableBarrierSet { return barrier_set_cast(BarrierSet::barrier_set()); } - G1CardTable* refinement_table() const { return _refinement_table; } + G1CardTable* refinement_table() const { return _refinement_table.load_relaxed(); } // Swap the global card table references, without synchronization. void swap_global_card_table(); diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp index 794e5db0634..54892c9191d 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -73,8 +73,8 @@ inline void G1BarrierSet::write_ref_field_post(T* field) { // Make sure that the card table reference is read only once. Otherwise the compiler // might reload that value in the two accesses below, that could cause writes to // the wrong card table. - CardTable* card_table = AtomicAccess::load(&_card_table); - CardValue* byte = card_table->byte_for(field); + CardTable* local_card_table = card_table(); + CardValue* byte = local_card_table->byte_for(field); if (*byte == G1CardTable::clean_card_val()) { *byte = G1CardTable::dirty_card_val(); } diff --git a/src/hotspot/share/gc/g1/g1CardSet.cpp b/src/hotspot/share/gc/g1/g1CardSet.cpp index 3441e6bc608..60ad63e812c 100644 --- a/src/hotspot/share/gc/g1/g1CardSet.cpp +++ b/src/hotspot/share/gc/g1/g1CardSet.cpp @@ -29,7 +29,6 @@ #include "gc/shared/gcLogPrecious.hpp" #include "gc/shared/gcTraceTime.inline.hpp" #include "memory/allocation.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/globals_extension.hpp" #include "runtime/java.hpp" #include "utilities/bitMap.inline.hpp" @@ -192,32 +191,32 @@ const char* G1CardSetConfiguration::mem_object_type_name_str(uint index) { void G1CardSetCoarsenStats::reset() { STATIC_ASSERT(ARRAY_SIZE(_coarsen_from) == ARRAY_SIZE(_coarsen_collision)); for (uint i = 0; i < ARRAY_SIZE(_coarsen_from); i++) { - _coarsen_from[i] = 0; - _coarsen_collision[i] = 0; + _coarsen_from[i].store_relaxed(0); + _coarsen_collision[i].store_relaxed(0); } } void G1CardSetCoarsenStats::set(G1CardSetCoarsenStats& other) { STATIC_ASSERT(ARRAY_SIZE(_coarsen_from) == ARRAY_SIZE(_coarsen_collision)); for (uint i = 0; i < ARRAY_SIZE(_coarsen_from); i++) { - _coarsen_from[i] = other._coarsen_from[i]; - _coarsen_collision[i] = other._coarsen_collision[i]; + _coarsen_from[i].store_relaxed(other._coarsen_from[i].load_relaxed()); + _coarsen_collision[i].store_relaxed(other._coarsen_collision[i].load_relaxed()); } } void G1CardSetCoarsenStats::subtract_from(G1CardSetCoarsenStats& other) { STATIC_ASSERT(ARRAY_SIZE(_coarsen_from) == ARRAY_SIZE(_coarsen_collision)); for (uint i = 0; i < ARRAY_SIZE(_coarsen_from); i++) { - _coarsen_from[i] = other._coarsen_from[i] - _coarsen_from[i]; - _coarsen_collision[i] = other._coarsen_collision[i] - _coarsen_collision[i]; + _coarsen_from[i].store_relaxed(other._coarsen_from[i].load_relaxed() - _coarsen_from[i].load_relaxed()); + _coarsen_collision[i].store_relaxed(other._coarsen_collision[i].load_relaxed() - _coarsen_collision[i].load_relaxed()); } } void G1CardSetCoarsenStats::record_coarsening(uint tag, bool collision) { assert(tag < ARRAY_SIZE(_coarsen_from), "tag %u out of bounds", tag); - AtomicAccess::inc(&_coarsen_from[tag], memory_order_relaxed); + _coarsen_from[tag].add_then_fetch(1u, memory_order_relaxed); if (collision) { - AtomicAccess::inc(&_coarsen_collision[tag], memory_order_relaxed); + _coarsen_collision[tag].add_then_fetch(1u, memory_order_relaxed); } } @@ -228,13 +227,13 @@ void G1CardSetCoarsenStats::print_on(outputStream* out) { "Inline->AoC %zu (%zu) " "AoC->BitMap %zu (%zu) " "BitMap->Full %zu (%zu) ", - _coarsen_from[0], _coarsen_collision[0], - _coarsen_from[1], _coarsen_collision[1], + _coarsen_from[0].load_relaxed(), _coarsen_collision[0].load_relaxed(), + _coarsen_from[1].load_relaxed(), _coarsen_collision[1].load_relaxed(), // There is no BitMap at the first level so we can't . - _coarsen_from[3], _coarsen_collision[3], - _coarsen_from[4], _coarsen_collision[4], - _coarsen_from[5], _coarsen_collision[5], - _coarsen_from[6], _coarsen_collision[6] + _coarsen_from[3].load_relaxed(), _coarsen_collision[3].load_relaxed(), + _coarsen_from[4].load_relaxed(), _coarsen_collision[4].load_relaxed(), + _coarsen_from[5].load_relaxed(), _coarsen_collision[5].load_relaxed(), + _coarsen_from[6].load_relaxed(), _coarsen_collision[6].load_relaxed() ); } @@ -248,7 +247,7 @@ class G1CardSetHashTable : public CHeapObj { // the per region cardsets. const static uint GroupBucketClaimSize = 4; // Did we insert at least one card in the table? - bool volatile _inserted_card; + Atomic _inserted_card; G1CardSetMemoryManager* _mm; CardSetHash _table; @@ -311,10 +310,10 @@ public: G1CardSetHashTableValue value(region_idx, G1CardSetInlinePtr()); bool inserted = _table.insert_get(Thread::current(), lookup, value, found, should_grow); - if (!_inserted_card && inserted) { + if (!_inserted_card.load_relaxed() && inserted) { // It does not matter to us who is setting the flag so a regular atomic store // is sufficient. - AtomicAccess::store(&_inserted_card, true); + _inserted_card.store_relaxed(true); } return found.value(); @@ -343,9 +342,9 @@ public: } void reset() { - if (AtomicAccess::load(&_inserted_card)) { + if (_inserted_card.load_relaxed()) { _table.unsafe_reset(InitialLogTableSize); - AtomicAccess::store(&_inserted_card, false); + _inserted_card.store_relaxed(false); } } @@ -455,14 +454,14 @@ void G1CardSet::free_mem_object(ContainerPtr container) { _mm->free(container_type_to_mem_object_type(type), value); } -G1CardSet::ContainerPtr G1CardSet::acquire_container(ContainerPtr volatile* container_addr) { +G1CardSet::ContainerPtr G1CardSet::acquire_container(Atomic* container_addr) { // Update reference counts under RCU critical section to avoid a // use-after-cleapup bug where we increment a reference count for // an object whose memory has already been cleaned up and reused. GlobalCounter::CriticalSection cs(Thread::current()); while (true) { // Get ContainerPtr and increment refcount atomically wrt to memory reuse. - ContainerPtr container = AtomicAccess::load_acquire(container_addr); + ContainerPtr container = container_addr->load_acquire(); uint cs_type = container_type(container); if (container == FullCardSet || cs_type == ContainerInlinePtr) { return container; @@ -503,15 +502,15 @@ class G1ReleaseCardsets : public StackObj { G1CardSet* _card_set; using ContainerPtr = G1CardSet::ContainerPtr; - void coarsen_to_full(ContainerPtr* container_addr) { + void coarsen_to_full(Atomic* container_addr) { while (true) { - ContainerPtr cur_container = AtomicAccess::load_acquire(container_addr); + ContainerPtr cur_container = container_addr->load_acquire(); uint cs_type = G1CardSet::container_type(cur_container); if (cur_container == G1CardSet::FullCardSet) { return; } - ContainerPtr old_value = AtomicAccess::cmpxchg(container_addr, cur_container, G1CardSet::FullCardSet); + ContainerPtr old_value = container_addr->compare_exchange(cur_container, G1CardSet::FullCardSet); if (old_value == cur_container) { _card_set->release_and_maybe_free_container(cur_container); @@ -523,7 +522,7 @@ class G1ReleaseCardsets : public StackObj { public: explicit G1ReleaseCardsets(G1CardSet* card_set) : _card_set(card_set) { } - void operator ()(ContainerPtr* container_addr) { + void operator ()(Atomic* container_addr) { coarsen_to_full(container_addr); } }; @@ -544,10 +543,10 @@ G1AddCardResult G1CardSet::add_to_howl(ContainerPtr parent_container, ContainerPtr container; uint bucket = _config->howl_bucket_index(card_in_region); - ContainerPtr volatile* bucket_entry = howl->container_addr(bucket); + Atomic* bucket_entry = howl->container_addr(bucket); while (true) { - if (AtomicAccess::load(&howl->_num_entries) >= _config->cards_in_howl_threshold()) { + if (howl->_num_entries.load_relaxed() >= _config->cards_in_howl_threshold()) { return Overflow; } @@ -571,7 +570,7 @@ G1AddCardResult G1CardSet::add_to_howl(ContainerPtr parent_container, } if (increment_total && add_result == Added) { - AtomicAccess::inc(&howl->_num_entries, memory_order_relaxed); + howl->_num_entries.add_then_fetch(1u, memory_order_relaxed); } if (to_transfer != nullptr) { @@ -588,7 +587,7 @@ G1AddCardResult G1CardSet::add_to_bitmap(ContainerPtr container, uint card_in_re return bitmap->add(card_offset, _config->cards_in_howl_bitmap_threshold(), _config->max_cards_in_howl_bitmap()); } -G1AddCardResult G1CardSet::add_to_inline_ptr(ContainerPtr volatile* container_addr, ContainerPtr container, uint card_in_region) { +G1AddCardResult G1CardSet::add_to_inline_ptr(Atomic* container_addr, ContainerPtr container, uint card_in_region) { G1CardSetInlinePtr value(container_addr, container); return value.add(card_in_region, _config->inline_ptr_bits_per_card(), _config->max_cards_in_inline_ptr()); } @@ -610,7 +609,7 @@ G1CardSet::ContainerPtr G1CardSet::create_coarsened_array_of_cards(uint card_in_ return new_container; } -bool G1CardSet::coarsen_container(ContainerPtr volatile* container_addr, +bool G1CardSet::coarsen_container(Atomic* container_addr, ContainerPtr cur_container, uint card_in_region, bool within_howl) { @@ -640,7 +639,7 @@ bool G1CardSet::coarsen_container(ContainerPtr volatile* container_addr, ShouldNotReachHere(); } - ContainerPtr old_value = AtomicAccess::cmpxchg(container_addr, cur_container, new_container); // Memory order? + ContainerPtr old_value = container_addr->compare_exchange(cur_container, new_container); // Memory order? if (old_value == cur_container) { // Success. Indicate that the cards from the current card set must be transferred // by this caller. @@ -687,7 +686,7 @@ void G1CardSet::transfer_cards(G1CardSetHashTableValue* table_entry, ContainerPt assert(container_type(source_container) == ContainerHowl, "must be"); // Need to correct for that the Full remembered set occupies more cards than the // AoCS before. - AtomicAccess::add(&_num_occupied, _config->max_cards_in_region() - table_entry->_num_occupied, memory_order_relaxed); + _num_occupied.add_then_fetch(_config->max_cards_in_region() - table_entry->_num_occupied.load_relaxed(), memory_order_relaxed); } } @@ -713,18 +712,18 @@ void G1CardSet::transfer_cards_in_howl(ContainerPtr parent_container, diff -= 1; G1CardSetHowl* howling_array = container_ptr(parent_container); - AtomicAccess::add(&howling_array->_num_entries, diff, memory_order_relaxed); + howling_array->_num_entries.add_then_fetch(diff, memory_order_relaxed); G1CardSetHashTableValue* table_entry = get_container(card_region); assert(table_entry != nullptr, "Table entry not found for transferred cards"); - AtomicAccess::add(&table_entry->_num_occupied, diff, memory_order_relaxed); + table_entry->_num_occupied.add_then_fetch(diff, memory_order_relaxed); - AtomicAccess::add(&_num_occupied, diff, memory_order_relaxed); + _num_occupied.add_then_fetch(diff, memory_order_relaxed); } } -G1AddCardResult G1CardSet::add_to_container(ContainerPtr volatile* container_addr, +G1AddCardResult G1CardSet::add_to_container(Atomic* container_addr, ContainerPtr container, uint card_region, uint card_in_region, @@ -827,8 +826,8 @@ G1AddCardResult G1CardSet::add_card(uint card_region, uint card_in_region, bool } if (increment_total && add_result == Added) { - AtomicAccess::inc(&table_entry->_num_occupied, memory_order_relaxed); - AtomicAccess::inc(&_num_occupied, memory_order_relaxed); + table_entry->_num_occupied.add_then_fetch(1u, memory_order_relaxed); + _num_occupied.add_then_fetch(1u, memory_order_relaxed); } if (should_grow_table) { _table->grow(); @@ -853,7 +852,7 @@ bool G1CardSet::contains_card(uint card_region, uint card_in_region) { return false; } - ContainerPtr container = table_entry->_container; + ContainerPtr container = table_entry->_container.load_relaxed(); if (container == FullCardSet) { // contains_card() is not a performance critical method so we do not hide that // case in the switch below. @@ -889,7 +888,7 @@ void G1CardSet::print_info(outputStream* st, uintptr_t card) { return; } - ContainerPtr container = table_entry->_container; + ContainerPtr container = table_entry->_container.load_relaxed(); if (container == FullCardSet) { st->print("FULL card set)"); return; @@ -940,7 +939,7 @@ void G1CardSet::iterate_cards_during_transfer(ContainerPtr const container, Card void G1CardSet::iterate_containers(ContainerPtrClosure* cl, bool at_safepoint) { auto do_value = [&] (G1CardSetHashTableValue* value) { - cl->do_containerptr(value->_region_idx, value->_num_occupied, value->_container); + cl->do_containerptr(value->_region_idx, value->_num_occupied.load_relaxed(), value->_container.load_relaxed()); return true; }; @@ -1001,11 +1000,11 @@ bool G1CardSet::occupancy_less_or_equal_to(size_t limit) const { } bool G1CardSet::is_empty() const { - return _num_occupied == 0; + return _num_occupied.load_relaxed() == 0; } size_t G1CardSet::occupied() const { - return _num_occupied; + return _num_occupied.load_relaxed(); } size_t G1CardSet::num_containers() { @@ -1051,7 +1050,7 @@ size_t G1CardSet::static_mem_size() { void G1CardSet::clear() { _table->reset(); - _num_occupied = 0; + _num_occupied.store_relaxed(0); _mm->flush(); } diff --git a/src/hotspot/share/gc/g1/g1CardSet.hpp b/src/hotspot/share/gc/g1/g1CardSet.hpp index 9cefc4b1c22..64ddf0ca6a4 100644 --- a/src/hotspot/share/gc/g1/g1CardSet.hpp +++ b/src/hotspot/share/gc/g1/g1CardSet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -27,6 +27,7 @@ #include "memory/allocation.hpp" #include "memory/memRegion.hpp" +#include "runtime/atomic.hpp" #include "utilities/concurrentHashTable.hpp" class G1CardSetAllocOptions; @@ -154,8 +155,8 @@ public: private: // Indices are "from" indices. - size_t _coarsen_from[NumCoarsenCategories]; - size_t _coarsen_collision[NumCoarsenCategories]; + Atomic _coarsen_from[NumCoarsenCategories]; + Atomic _coarsen_collision[NumCoarsenCategories]; public: G1CardSetCoarsenStats() { reset(); } @@ -271,11 +272,11 @@ private: // Total number of cards in this card set. This is a best-effort value, i.e. there may // be (slightly) more cards in the card set than this value in reality. - size_t _num_occupied; + Atomic _num_occupied; ContainerPtr make_container_ptr(void* value, uintptr_t type); - ContainerPtr acquire_container(ContainerPtr volatile* container_addr); + ContainerPtr acquire_container(Atomic* container_addr); // Returns true if the card set container should be released bool release_container(ContainerPtr container); // Release card set and free if needed. @@ -288,7 +289,7 @@ private: // coarsen_container does not transfer cards from cur_container // to the new container. Transfer is achieved by transfer_cards. // Returns true if this was the thread that coarsened the container (and added the card). - bool coarsen_container(ContainerPtr volatile* container_addr, + bool coarsen_container(Atomic* container_addr, ContainerPtr cur_container, uint card_in_region, bool within_howl = false); @@ -300,9 +301,9 @@ private: void transfer_cards(G1CardSetHashTableValue* table_entry, ContainerPtr source_container, uint card_region); void transfer_cards_in_howl(ContainerPtr parent_container, ContainerPtr source_container, uint card_region); - G1AddCardResult add_to_container(ContainerPtr volatile* container_addr, ContainerPtr container, uint card_region, uint card, bool increment_total = true); + G1AddCardResult add_to_container(Atomic* container_addr, ContainerPtr container, uint card_region, uint card, bool increment_total = true); - G1AddCardResult add_to_inline_ptr(ContainerPtr volatile* container_addr, ContainerPtr container, uint card_in_region); + G1AddCardResult add_to_inline_ptr(Atomic* container_addr, ContainerPtr container, uint card_in_region); G1AddCardResult add_to_array(ContainerPtr container, uint card_in_region); G1AddCardResult add_to_bitmap(ContainerPtr container, uint card_in_region); G1AddCardResult add_to_howl(ContainerPtr parent_container, uint card_region, uint card_in_region, bool increment_total = true); @@ -366,7 +367,6 @@ public: size_t num_containers(); - static G1CardSetCoarsenStats coarsen_stats(); static void print_coarsen_stats(outputStream* out); // Returns size of the actual remembered set containers in bytes. @@ -412,8 +412,15 @@ public: using ContainerPtr = G1CardSet::ContainerPtr; const uint _region_idx; - uint volatile _num_occupied; - ContainerPtr volatile _container; + Atomic _num_occupied; + Atomic _container; + + // Copy constructor needed for use in ConcurrentHashTable. + G1CardSetHashTableValue(const G1CardSetHashTableValue& other) : + _region_idx(other._region_idx), + _num_occupied(other._num_occupied.load_relaxed()), + _container(other._container.load_relaxed()) + { } G1CardSetHashTableValue(uint region_idx, ContainerPtr container) : _region_idx(region_idx), _num_occupied(0), _container(container) { } }; diff --git a/src/hotspot/share/gc/g1/g1CardSetContainers.hpp b/src/hotspot/share/gc/g1/g1CardSetContainers.hpp index 72c7795be2e..78551479e06 100644 --- a/src/hotspot/share/gc/g1/g1CardSetContainers.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetContainers.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, 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 @@ -27,7 +27,7 @@ #include "gc/g1/g1CardSet.hpp" #include "memory/allocation.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "utilities/bitMap.hpp" #include "utilities/globalDefinitions.hpp" @@ -67,7 +67,7 @@ class G1CardSetInlinePtr : public StackObj { using ContainerPtr = G1CardSet::ContainerPtr; - ContainerPtr volatile * _value_addr; + Atomic* _value_addr; ContainerPtr _value; static const uint SizeFieldLen = 3; @@ -103,7 +103,7 @@ public: explicit G1CardSetInlinePtr(ContainerPtr value) : G1CardSetInlinePtr(nullptr, value) {} - G1CardSetInlinePtr(ContainerPtr volatile* value_addr, ContainerPtr value) : _value_addr(value_addr), _value(value) { + G1CardSetInlinePtr(Atomic* value_addr, ContainerPtr value) : _value_addr(value_addr), _value(value) { assert(G1CardSet::container_type(_value) == G1CardSet::ContainerInlinePtr, "Value " PTR_FORMAT " is not a valid G1CardSetInlinePtr.", p2i(_value)); } @@ -145,13 +145,13 @@ public: // All but inline pointers are of this kind. For those, card entries are stored // directly in the ContainerPtr of the ConcurrentHashTable node. class G1CardSetContainer { - uintptr_t _ref_count; + Atomic _ref_count; protected: ~G1CardSetContainer() = default; public: G1CardSetContainer() : _ref_count(3) { } - uintptr_t refcount() const { return AtomicAccess::load_acquire(&_ref_count); } + uintptr_t refcount() const { return _ref_count.load_acquire(); } bool try_increment_refcount(); @@ -172,7 +172,7 @@ public: using ContainerPtr = G1CardSet::ContainerPtr; private: EntryCountType _size; - EntryCountType volatile _num_entries; + Atomic _num_entries; // VLA implementation. EntryDataType _data[1]; @@ -180,10 +180,10 @@ private: static const EntryCountType EntryMask = LockBitMask - 1; class G1CardSetArrayLocker : public StackObj { - EntryCountType volatile* _num_entries_addr; + Atomic* _num_entries_addr; EntryCountType _local_num_entries; public: - G1CardSetArrayLocker(EntryCountType volatile* value); + G1CardSetArrayLocker(Atomic* value); EntryCountType num_entries() const { return _local_num_entries; } void inc_num_entries() { @@ -192,7 +192,7 @@ private: } ~G1CardSetArrayLocker() { - AtomicAccess::release_store(_num_entries_addr, _local_num_entries); + _num_entries_addr->release_store(_local_num_entries); } }; @@ -213,7 +213,7 @@ public: template void iterate(CardVisitor& found); - size_t num_entries() const { return _num_entries & EntryMask; } + size_t num_entries() const { return _num_entries.load_relaxed() & EntryMask; } static size_t header_size_in_bytes(); @@ -223,7 +223,7 @@ public: }; class G1CardSetBitMap : public G1CardSetContainer { - size_t _num_bits_set; + Atomic _num_bits_set; BitMap::bm_word_t _bits[1]; public: @@ -236,7 +236,7 @@ public: return bm.at(card_idx); } - uint num_bits_set() const { return (uint)_num_bits_set; } + uint num_bits_set() const { return (uint)_num_bits_set.load_relaxed(); } template void iterate(CardVisitor& found, size_t const size_in_bits, uint offset); @@ -255,10 +255,10 @@ class G1CardSetHowl : public G1CardSetContainer { public: typedef uint EntryCountType; using ContainerPtr = G1CardSet::ContainerPtr; - EntryCountType volatile _num_entries; + Atomic _num_entries; private: // VLA implementation. - ContainerPtr _buckets[1]; + Atomic _buckets[1]; // Do not add class member variables beyond this point. // Iterates over the given ContainerPtr with at index in this Howl card set, @@ -268,14 +268,14 @@ private: ContainerPtr at(EntryCountType index) const; - ContainerPtr const* buckets() const; + Atomic const* buckets() const; public: G1CardSetHowl(EntryCountType card_in_region, G1CardSetConfiguration* config); - ContainerPtr const* container_addr(EntryCountType index) const; + Atomic const* container_addr(EntryCountType index) const; - ContainerPtr* container_addr(EntryCountType index); + Atomic* container_addr(EntryCountType index); bool contains(uint card_idx, G1CardSetConfiguration* config); // Iterates over all ContainerPtrs in this Howl card set, applying a CardOrRangeVisitor diff --git a/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp b/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp index 1958309f517..3c6fb9d1a02 100644 --- a/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CardSetContainers.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -67,7 +67,7 @@ inline G1AddCardResult G1CardSetInlinePtr::add(uint card_idx, uint bits_per_card return Overflow; } ContainerPtr new_value = merge(_value, card_idx, num_cards, bits_per_card); - ContainerPtr old_value = AtomicAccess::cmpxchg(_value_addr, _value, new_value, memory_order_relaxed); + ContainerPtr old_value = _value_addr->compare_exchange(_value, new_value, memory_order_relaxed); if (_value == old_value) { return Added; } @@ -126,7 +126,7 @@ inline bool G1CardSetContainer::try_increment_refcount() { } uintptr_t new_value = old_value + 2; - uintptr_t ref_count = AtomicAccess::cmpxchg(&_ref_count, old_value, new_value); + uintptr_t ref_count = _ref_count.compare_exchange(old_value, new_value); if (ref_count == old_value) { return true; } @@ -137,7 +137,7 @@ inline bool G1CardSetContainer::try_increment_refcount() { inline uintptr_t G1CardSetContainer::decrement_refcount() { uintptr_t old_value = refcount(); assert((old_value & 0x1) != 0 && old_value >= 3, "precondition"); - return AtomicAccess::sub(&_ref_count, 2u); + return _ref_count.sub_then_fetch(2u); } inline G1CardSetArray::G1CardSetArray(uint card_in_region, EntryCountType num_cards) : @@ -149,14 +149,13 @@ inline G1CardSetArray::G1CardSetArray(uint card_in_region, EntryCountType num_ca *entry_addr(0) = checked_cast(card_in_region); } -inline G1CardSetArray::G1CardSetArrayLocker::G1CardSetArrayLocker(EntryCountType volatile* num_entries_addr) : +inline G1CardSetArray::G1CardSetArrayLocker::G1CardSetArrayLocker(Atomic* num_entries_addr) : _num_entries_addr(num_entries_addr) { SpinYield s; - EntryCountType num_entries = AtomicAccess::load(_num_entries_addr) & EntryMask; + EntryCountType num_entries = _num_entries_addr->load_relaxed() & EntryMask; while (true) { - EntryCountType old_value = AtomicAccess::cmpxchg(_num_entries_addr, - num_entries, - (EntryCountType)(num_entries | LockBitMask)); + EntryCountType old_value = _num_entries_addr->compare_exchange(num_entries, + (EntryCountType)(num_entries | LockBitMask)); if (old_value == num_entries) { // Succeeded locking the array. _local_num_entries = num_entries; @@ -174,7 +173,7 @@ inline G1CardSetArray::EntryDataType const* G1CardSetArray::base_addr() const { } inline G1CardSetArray::EntryDataType const* G1CardSetArray::entry_addr(EntryCountType index) const { - assert(index < _num_entries, "precondition"); + assert(index < _num_entries.load_relaxed(), "precondition"); return base_addr() + index; } @@ -189,7 +188,7 @@ inline G1CardSetArray::EntryDataType G1CardSetArray::at(EntryCountType index) co inline G1AddCardResult G1CardSetArray::add(uint card_idx) { assert(card_idx < (1u << (sizeof(EntryDataType) * BitsPerByte)), "Card index %u does not fit allowed card value range.", card_idx); - EntryCountType num_entries = AtomicAccess::load_acquire(&_num_entries) & EntryMask; + EntryCountType num_entries = _num_entries.load_acquire() & EntryMask; EntryCountType idx = 0; for (; idx < num_entries; idx++) { if (at(idx) == card_idx) { @@ -223,7 +222,7 @@ inline G1AddCardResult G1CardSetArray::add(uint card_idx) { } inline bool G1CardSetArray::contains(uint card_idx) { - EntryCountType num_entries = AtomicAccess::load_acquire(&_num_entries) & EntryMask; + EntryCountType num_entries = _num_entries.load_acquire() & EntryMask; for (EntryCountType idx = 0; idx < num_entries; idx++) { if (at(idx) == card_idx) { @@ -235,7 +234,7 @@ inline bool G1CardSetArray::contains(uint card_idx) { template void G1CardSetArray::iterate(CardVisitor& found) { - EntryCountType num_entries = AtomicAccess::load_acquire(&_num_entries) & EntryMask; + EntryCountType num_entries = _num_entries.load_acquire() & EntryMask; for (EntryCountType idx = 0; idx < num_entries; idx++) { found(at(idx)); } @@ -256,11 +255,11 @@ inline G1CardSetBitMap::G1CardSetBitMap(uint card_in_region, uint size_in_bits) inline G1AddCardResult G1CardSetBitMap::add(uint card_idx, size_t threshold, size_t size_in_bits) { BitMapView bm(_bits, size_in_bits); - if (_num_bits_set >= threshold) { + if (_num_bits_set.load_relaxed() >= threshold) { return bm.at(card_idx) ? Found : Overflow; } if (bm.par_set_bit(card_idx)) { - AtomicAccess::inc(&_num_bits_set, memory_order_relaxed); + _num_bits_set.add_then_fetch(1u, memory_order_relaxed); return Added; } return Found; @@ -276,22 +275,22 @@ inline size_t G1CardSetBitMap::header_size_in_bytes() { return offset_of(G1CardSetBitMap, _bits); } -inline G1CardSetHowl::ContainerPtr const* G1CardSetHowl::container_addr(EntryCountType index) const { - assert(index < _num_entries, "precondition"); +inline Atomic const* G1CardSetHowl::container_addr(EntryCountType index) const { + assert(index < _num_entries.load_relaxed(), "precondition"); return buckets() + index; } -inline G1CardSetHowl::ContainerPtr* G1CardSetHowl::container_addr(EntryCountType index) { - return const_cast(const_cast(this)->container_addr(index)); +inline Atomic* G1CardSetHowl::container_addr(EntryCountType index) { + return const_cast*>(const_cast(this)->container_addr(index)); } inline G1CardSetHowl::ContainerPtr G1CardSetHowl::at(EntryCountType index) const { - return *container_addr(index); + return (*container_addr(index)).load_relaxed(); } -inline G1CardSetHowl::ContainerPtr const* G1CardSetHowl::buckets() const { +inline Atomic const* G1CardSetHowl::buckets() const { const void* ptr = reinterpret_cast(this) + header_size_in_bytes(); - return reinterpret_cast(ptr); + return reinterpret_cast const*>(ptr); } inline G1CardSetHowl::G1CardSetHowl(EntryCountType card_in_region, G1CardSetConfiguration* config) : @@ -300,7 +299,7 @@ inline G1CardSetHowl::G1CardSetHowl(EntryCountType card_in_region, G1CardSetConf EntryCountType num_buckets = config->num_buckets_in_howl(); EntryCountType bucket = config->howl_bucket_index(card_in_region); for (uint i = 0; i < num_buckets; ++i) { - *container_addr(i) = G1CardSetInlinePtr(); + container_addr(i)->store_relaxed(G1CardSetInlinePtr()); if (i == bucket) { G1CardSetInlinePtr value(container_addr(i), at(i)); value.add(card_in_region, config->inline_ptr_bits_per_card(), config->max_cards_in_inline_ptr()); @@ -310,8 +309,8 @@ inline G1CardSetHowl::G1CardSetHowl(EntryCountType card_in_region, G1CardSetConf inline bool G1CardSetHowl::contains(uint card_idx, G1CardSetConfiguration* config) { EntryCountType bucket = config->howl_bucket_index(card_idx); - ContainerPtr* array_entry = container_addr(bucket); - ContainerPtr container = AtomicAccess::load_acquire(array_entry); + Atomic* array_entry = container_addr(bucket); + ContainerPtr container = array_entry->load_acquire(); switch (G1CardSet::container_type(container)) { case G1CardSet::ContainerArrayOfCards: { diff --git a/src/hotspot/share/gc/g1/g1CardSetMemory.cpp b/src/hotspot/share/gc/g1/g1CardSetMemory.cpp index d13a6fe2dca..60602ef942b 100644 --- a/src/hotspot/share/gc/g1/g1CardSetMemory.cpp +++ b/src/hotspot/share/gc/g1/g1CardSetMemory.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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,6 @@ #include "gc/g1/g1CardSetContainers.inline.hpp" #include "gc/g1/g1CardSetMemory.inline.hpp" #include "gc/g1/g1MonotonicArena.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/ostream.hpp" G1CardSetAllocator::G1CardSetAllocator(const char* name, diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index b6c3c0b0907..9424a804bd8 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -525,7 +525,7 @@ void G1CollectedHeap::iterate_regions_in_range(MemRegion range, const Func& func } } -HeapWord* G1CollectedHeap::alloc_archive_region(size_t word_size, HeapWord* preferred_addr) { +HeapWord* G1CollectedHeap::alloc_archive_region(size_t word_size) { assert(!is_init_completed(), "Expect to be called at JVM init time"); MutexLocker x(Heap_lock); @@ -1320,7 +1320,6 @@ G1CollectedHeap::G1CollectedHeap() : _card_set_freelist_pool(G1CardSetConfiguration::num_mem_object_types()), _young_regions_cset_group(card_set_config(), &_card_set_freelist_pool, G1CSetCandidateGroup::YoungRegionId), _cm(nullptr), - _cm_thread(nullptr), _cr(nullptr), _task_queues(nullptr), _partial_array_state_manager(nullptr), @@ -1564,7 +1563,6 @@ jint G1CollectedHeap::initialize() { // Create the G1ConcurrentMark data structure and thread. // (Must do this late, so that "max_[reserved_]regions" is defined.) _cm = new G1ConcurrentMark(this, bitmap_storage); - _cm_thread = _cm->cm_thread(); // Now expand into the initial heap size. if (!expand(init_byte_size, _workers)) { @@ -1636,7 +1634,9 @@ jint G1CollectedHeap::initialize() { } bool G1CollectedHeap::concurrent_mark_is_terminating() const { - return _cm_thread->should_terminate(); + assert(_cm != nullptr, "_cm must have been created"); + assert(_cm->is_fully_initialized(), "thread must exist in order to check if mark is terminating"); + return _cm->cm_thread()->should_terminate(); } void G1CollectedHeap::stop() { @@ -1645,7 +1645,9 @@ void G1CollectedHeap::stop() { // that are destroyed during shutdown. _cr->stop(); _service_thread->stop(); - _cm_thread->stop(); + if (_cm->is_fully_initialized()) { + _cm->cm_thread()->stop(); + } } void G1CollectedHeap::safepoint_synchronize_begin() { @@ -1842,7 +1844,7 @@ void G1CollectedHeap::increment_old_marking_cycles_completed(bool concurrent, // is set) so that if a waiter requests another System.gc() it doesn't // incorrectly see that a marking cycle is still in progress. if (concurrent) { - _cm_thread->set_idle(); + _cm->cm_thread()->set_idle(); } // Notify threads waiting in System.gc() (with ExplicitGCInvokesConcurrent) @@ -2421,7 +2423,6 @@ void G1CollectedHeap::print_gc_on(outputStream* st) const { void G1CollectedHeap::gc_threads_do(ThreadClosure* tc) const { workers()->threads_do(tc); - tc->do_thread(_cm_thread); _cm->threads_do(tc); _cr->threads_do(tc); tc->do_thread(_service_thread); @@ -2542,15 +2543,15 @@ HeapWord* G1CollectedHeap::do_collection_pause(size_t word_size, } void G1CollectedHeap::start_concurrent_cycle(bool concurrent_operation_is_full_mark) { - assert(!_cm_thread->in_progress(), "Can not start concurrent operation while in progress"); - + assert(_cm->is_fully_initialized(), "sanity"); + assert(!_cm->in_progress(), "Can not start concurrent operation while in progress"); MutexLocker x(G1CGC_lock, Mutex::_no_safepoint_check_flag); if (concurrent_operation_is_full_mark) { _cm->post_concurrent_mark_start(); - _cm_thread->start_full_mark(); + _cm->cm_thread()->start_full_mark(); } else { _cm->post_concurrent_undo_start(); - _cm_thread->start_undo_mark(); + _cm->cm_thread()->start_undo_mark(); } G1CGC_lock->notify(); } @@ -2726,6 +2727,8 @@ void G1CollectedHeap::do_collection_pause_at_safepoint(size_t allocation_word_si _bytes_used_during_gc = 0; + _cm->fully_initialize(); + policy()->decide_on_concurrent_start_pause(); // Record whether this pause may need to trigger a concurrent operation. Later, // when we signal the G1ConcurrentMarkThread, the collector state has already diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 8009df1fa6a..8ff9d481000 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -736,12 +736,10 @@ public: void iterate_regions_in_range(MemRegion range, const Func& func); // Commit the required number of G1 region(s) according to the size requested - // and mark them as 'old' region(s). Preferred address is treated as a hint for - // the location of the archive space in the heap. The returned address may or may - // not be same as the preferred address. + // and mark them as 'old' region(s). // This API is only used for allocating heap space for the archived heap objects // in the CDS archive. - HeapWord* alloc_archive_region(size_t word_size, HeapWord* preferred_addr); + HeapWord* alloc_archive_region(size_t word_size); // Populate the G1BlockOffsetTable for archived regions with the given // memory range. @@ -825,7 +823,6 @@ public: // The concurrent marker (and the thread it runs in.) G1ConcurrentMark* _cm; - G1ConcurrentMarkThread* _cm_thread; // The concurrent refiner. G1ConcurrentRefine* _cr; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index 958b171444e..8782b65b6f9 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -31,6 +31,7 @@ #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1ConcurrentMark.inline.hpp" #include "gc/g1/g1EvacFailureRegions.hpp" +#include "gc/g1/g1EvacStats.inline.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionManager.inline.hpp" #include "gc/g1/g1HeapRegionRemSet.hpp" diff --git a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp b/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp index d9496410c12..e7bab32129e 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetChooser.cpp @@ -203,13 +203,13 @@ class G1BuildCandidateRegionsTask : public WorkerTask { G1CollectedHeap* _g1h; G1HeapRegionClaimer _hrclaimer; - uint volatile _num_regions_added; + Atomic _num_regions_added; G1BuildCandidateArray _result; void update_totals(uint num_regions) { if (num_regions > 0) { - AtomicAccess::add(&_num_regions_added, num_regions); + _num_regions_added.add_then_fetch(num_regions); } } @@ -221,7 +221,7 @@ class G1BuildCandidateRegionsTask : public WorkerTask { void prune(G1HeapRegion** data) { G1Policy* p = G1CollectedHeap::heap()->policy(); - uint num_candidates = AtomicAccess::load(&_num_regions_added); + uint num_candidates = _num_regions_added.load_relaxed(); uint min_old_cset_length = p->calc_min_old_cset_length(num_candidates); uint num_pruned = 0; @@ -254,7 +254,7 @@ class G1BuildCandidateRegionsTask : public WorkerTask { wasted_bytes, allowed_waste); - AtomicAccess::sub(&_num_regions_added, num_pruned, memory_order_relaxed); + _num_regions_added.sub_then_fetch(num_pruned, memory_order_relaxed); } public: @@ -275,7 +275,7 @@ public: _result.sort_by_gc_efficiency(); prune(_result.array()); candidates->set_candidates_from_marking(_result.array(), - _num_regions_added); + _num_regions_added.load_relaxed()); } }; diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 52591f7ce5f..5f096c2b9d7 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -291,9 +291,9 @@ void G1CMMarkStack::expand() { _chunk_allocator.try_expand(); } -void G1CMMarkStack::add_chunk_to_list(TaskQueueEntryChunk* volatile* list, TaskQueueEntryChunk* elem) { - elem->next = *list; - *list = elem; +void G1CMMarkStack::add_chunk_to_list(Atomic* list, TaskQueueEntryChunk* elem) { + elem->next = list->load_relaxed(); + list->store_relaxed(elem); } void G1CMMarkStack::add_chunk_to_chunk_list(TaskQueueEntryChunk* elem) { @@ -307,10 +307,10 @@ void G1CMMarkStack::add_chunk_to_free_list(TaskQueueEntryChunk* elem) { add_chunk_to_list(&_free_list, elem); } -G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::remove_chunk_from_list(TaskQueueEntryChunk* volatile* list) { - TaskQueueEntryChunk* result = *list; +G1CMMarkStack::TaskQueueEntryChunk* G1CMMarkStack::remove_chunk_from_list(Atomic* list) { + TaskQueueEntryChunk* result = list->load_relaxed(); if (result != nullptr) { - *list = (*list)->next; + list->store_relaxed(list->load_relaxed()->next); } return result; } @@ -364,8 +364,8 @@ bool G1CMMarkStack::par_pop_chunk(G1TaskQueueEntry* ptr_arr) { void G1CMMarkStack::set_empty() { _chunks_in_chunk_list = 0; - _chunk_list = nullptr; - _free_list = nullptr; + _chunk_list.store_relaxed(nullptr); + _free_list.store_relaxed(nullptr); _chunk_allocator.reset(); } @@ -382,12 +382,12 @@ G1CMRootMemRegions::~G1CMRootMemRegions() { } void G1CMRootMemRegions::reset() { - _num_root_regions = 0; + _num_root_regions.store_relaxed(0); } void G1CMRootMemRegions::add(HeapWord* start, HeapWord* end) { assert_at_safepoint(); - size_t idx = AtomicAccess::fetch_then_add(&_num_root_regions, 1u); + size_t idx = _num_root_regions.fetch_then_add(1u); assert(idx < _max_regions, "Trying to add more root MemRegions than there is space %zu", _max_regions); assert(start != nullptr && end != nullptr && start <= end, "Start (" PTR_FORMAT ") should be less or equal to " "end (" PTR_FORMAT ")", p2i(start), p2i(end)); @@ -398,36 +398,38 @@ void G1CMRootMemRegions::add(HeapWord* start, HeapWord* end) { void G1CMRootMemRegions::prepare_for_scan() { assert(!scan_in_progress(), "pre-condition"); - _scan_in_progress = _num_root_regions > 0; + _scan_in_progress.store_relaxed(num_root_regions() > 0); - _claimed_root_regions = 0; - _should_abort = false; + _claimed_root_regions.store_relaxed(0); + _should_abort.store_relaxed(false); } const MemRegion* G1CMRootMemRegions::claim_next() { - if (_should_abort) { + if (_should_abort.load_relaxed()) { // If someone has set the should_abort flag, we return null to // force the caller to bail out of their loop. return nullptr; } - if (_claimed_root_regions >= _num_root_regions) { + uint local_num_root_regions = num_root_regions(); + if (_claimed_root_regions.load_relaxed() >= local_num_root_regions) { return nullptr; } - size_t claimed_index = AtomicAccess::fetch_then_add(&_claimed_root_regions, 1u); - if (claimed_index < _num_root_regions) { + size_t claimed_index = _claimed_root_regions.fetch_then_add(1u); + if (claimed_index < local_num_root_regions) { return &_root_regions[claimed_index]; } return nullptr; } uint G1CMRootMemRegions::num_root_regions() const { - return (uint)_num_root_regions; + return (uint)_num_root_regions.load_relaxed(); } bool G1CMRootMemRegions::contains(const MemRegion mr) const { - for (uint i = 0; i < _num_root_regions; i++) { + uint local_num_root_regions = num_root_regions(); + for (uint i = 0; i < local_num_root_regions; i++) { if (_root_regions[i].equals(mr)) { return true; } @@ -437,7 +439,7 @@ bool G1CMRootMemRegions::contains(const MemRegion mr) const { void G1CMRootMemRegions::notify_scan_done() { MutexLocker x(G1RootRegionScan_lock, Mutex::_no_safepoint_check_flag); - _scan_in_progress = false; + _scan_in_progress.store_relaxed(false); G1RootRegionScan_lock->notify_all(); } @@ -448,10 +450,10 @@ void G1CMRootMemRegions::cancel_scan() { void G1CMRootMemRegions::scan_finished() { assert(scan_in_progress(), "pre-condition"); - if (!_should_abort) { - assert(_claimed_root_regions >= num_root_regions(), + if (!_should_abort.load_relaxed()) { + assert(_claimed_root_regions.load_relaxed() >= num_root_regions(), "we should have claimed all root regions, claimed %zu, length = %u", - _claimed_root_regions, num_root_regions()); + _claimed_root_regions.load_relaxed(), num_root_regions()); } notify_scan_done(); @@ -473,7 +475,7 @@ bool G1CMRootMemRegions::wait_until_scan_finished() { G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, G1RegionToSpaceMapper* bitmap_storage) : - // _cm_thread set inside the constructor + _cm_thread(nullptr), _g1h(g1h), _mark_bitmap(), @@ -484,13 +486,12 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, _global_mark_stack(), - // _finger set in set_non_marking_state + _finger(nullptr), // _finger set in set_non_marking_state _worker_id_offset(G1ConcRefinementThreads), // The refinement control thread does not refine cards, so it's just the worker threads. _max_num_tasks(MAX2(ConcGCThreads, ParallelGCThreads)), - // _num_active_tasks set in set_non_marking_state() - // _tasks set inside the constructor - + _num_active_tasks(0), // _num_active_tasks set in set_non_marking_state() + _tasks(nullptr), // _tasks set inside late_init() _task_queues(new G1CMTaskQueueSet(_max_num_tasks)), _terminator(_max_num_tasks, _task_queues), _partial_array_state_manager(new PartialArrayStateManager(_max_num_tasks)), @@ -525,6 +526,12 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, assert(G1CGC_lock != nullptr, "CGC_lock must be initialized"); _mark_bitmap.initialize(g1h->reserved(), bitmap_storage); +} + +void G1ConcurrentMark::fully_initialize() { + if (is_fully_initialized()) { + return; + } // Create & start ConcurrentMark thread. _cm_thread = new G1ConcurrentMarkThread(this); @@ -560,6 +567,10 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, reset_at_marking_complete(); } +bool G1ConcurrentMark::in_progress() const { + return is_fully_initialized() ? _cm_thread->in_progress() : false; +} + PartialArrayStateManager* G1ConcurrentMark::partial_array_state_manager() const { return _partial_array_state_manager; } @@ -765,7 +776,7 @@ private: // as asserts here to minimize their overhead on the product. However, we // will have them as guarantees at the beginning / end of the bitmap // clearing to get some checking in the product. - assert(!suspendible() || _cm->cm_thread()->in_progress(), "invariant"); + assert(!suspendible() || _cm->in_progress(), "invariant"); assert(!suspendible() || !G1CollectedHeap::heap()->collector_state()->mark_or_rebuild_in_progress(), "invariant"); // Abort iteration if necessary. @@ -821,7 +832,8 @@ void G1ConcurrentMark::clear_bitmap(WorkerThreads* workers, bool may_yield) { void G1ConcurrentMark::cleanup_for_next_mark() { // Make sure that the concurrent mark thread looks to still be in // the current cycle. - guarantee(cm_thread()->in_progress(), "invariant"); + guarantee(is_fully_initialized(), "should be initializd"); + guarantee(in_progress(), "invariant"); // We are finishing up the current cycle by clearing the next // marking bitmap and getting it ready for the next cycle. During @@ -834,7 +846,8 @@ void G1ConcurrentMark::cleanup_for_next_mark() { reset_partial_array_state_manager(); // Repeat the asserts from above. - guarantee(cm_thread()->in_progress(), "invariant"); + guarantee(is_fully_initialized(), "should be initializd"); + guarantee(in_progress(), "invariant"); guarantee(!_g1h->collector_state()->mark_or_rebuild_in_progress(), "invariant"); } @@ -1925,7 +1938,8 @@ bool G1ConcurrentMark::concurrent_cycle_abort() { // nothing, but this situation should be extremely rare (a full gc after shutdown // has been signalled is already rare), and this work should be negligible compared // to actual full gc work. - if (!cm_thread()->in_progress() && !_g1h->concurrent_mark_is_terminating()) { + + if (!is_fully_initialized() || (!cm_thread()->in_progress() && !_g1h->concurrent_mark_is_terminating())) { return false; } @@ -1987,6 +2001,10 @@ void G1ConcurrentMark::print_summary_info() { } log.trace(" Concurrent marking:"); + if (!is_fully_initialized()) { + log.trace(" has not been initialized yet"); + return; + } print_ms_time_info(" ", "remarks", _remark_times); { print_ms_time_info(" ", "final marks", _remark_mark_times); @@ -2003,7 +2021,10 @@ void G1ConcurrentMark::print_summary_info() { } void G1ConcurrentMark::threads_do(ThreadClosure* tc) const { - _concurrent_workers->threads_do(tc); + if (is_fully_initialized()) { // they are initialized late + tc->do_thread(_cm_thread); + _concurrent_workers->threads_do(tc); + } } void G1ConcurrentMark::print_on(outputStream* st) const { diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 52a1b133439..3a4cbf1b83e 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -210,17 +210,17 @@ private: ChunkAllocator _chunk_allocator; char _pad0[DEFAULT_PADDING_SIZE]; - TaskQueueEntryChunk* volatile _free_list; // Linked list of free chunks that can be allocated by users. + Atomic _free_list; // Linked list of free chunks that can be allocated by users. char _pad1[DEFAULT_PADDING_SIZE - sizeof(TaskQueueEntryChunk*)]; - TaskQueueEntryChunk* volatile _chunk_list; // List of chunks currently containing data. + Atomic _chunk_list; // List of chunks currently containing data. volatile size_t _chunks_in_chunk_list; char _pad2[DEFAULT_PADDING_SIZE - sizeof(TaskQueueEntryChunk*) - sizeof(size_t)]; // Atomically add the given chunk to the list. - void add_chunk_to_list(TaskQueueEntryChunk* volatile* list, TaskQueueEntryChunk* elem); + void add_chunk_to_list(Atomic* list, TaskQueueEntryChunk* elem); // Atomically remove and return a chunk from the given list. Returns null if the // list is empty. - TaskQueueEntryChunk* remove_chunk_from_list(TaskQueueEntryChunk* volatile* list); + TaskQueueEntryChunk* remove_chunk_from_list(Atomic* list); void add_chunk_to_chunk_list(TaskQueueEntryChunk* elem); void add_chunk_to_free_list(TaskQueueEntryChunk* elem); @@ -252,7 +252,7 @@ private: // Return whether the chunk list is empty. Racy due to unsynchronized access to // _chunk_list. - bool is_empty() const { return _chunk_list == nullptr; } + bool is_empty() const { return _chunk_list.load_relaxed() == nullptr; } size_t capacity() const { return _chunk_allocator.capacity(); } @@ -290,12 +290,12 @@ class G1CMRootMemRegions { MemRegion* _root_regions; size_t const _max_regions; - volatile size_t _num_root_regions; // Actual number of root regions. + Atomic _num_root_regions; // Actual number of root regions. - volatile size_t _claimed_root_regions; // Number of root regions currently claimed. + Atomic _claimed_root_regions; // Number of root regions currently claimed. - volatile bool _scan_in_progress; - volatile bool _should_abort; + Atomic _scan_in_progress; + Atomic _should_abort; void notify_scan_done(); @@ -312,11 +312,11 @@ public: void prepare_for_scan(); // Forces get_next() to return null so that the iteration aborts early. - void abort() { _should_abort = true; } + void abort() { _should_abort.store_relaxed(true); } // Return true if the CM thread are actively scanning root regions, // false otherwise. - bool scan_in_progress() { return _scan_in_progress; } + bool scan_in_progress() { return _scan_in_progress.load_relaxed(); } // Claim the next root MemRegion to scan atomically, or return null if // all have been claimed. @@ -555,6 +555,9 @@ public: uint worker_id_offset() const { return _worker_id_offset; } + void fully_initialize(); + bool is_fully_initialized() const { return _cm_thread != nullptr; } + bool in_progress() const; uint max_num_tasks() const {return _max_num_tasks; } // Clear statistics gathered during the concurrent cycle for the given region after diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp index fe72c68d4eb..2f4824e4cae 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp @@ -90,7 +90,7 @@ inline void G1CMMarkStack::iterate(Fn fn) const { size_t num_chunks = 0; - TaskQueueEntryChunk* cur = _chunk_list; + TaskQueueEntryChunk* cur = _chunk_list.load_relaxed(); while (cur != nullptr) { guarantee(num_chunks <= _chunks_in_chunk_list, "Found %zu oop chunks which is more than there should be", num_chunks); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp index ed6a9ad4292..8546e6e2d64 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp @@ -28,6 +28,7 @@ #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" +#include "gc/g1/g1ConcurrentRefineStats.inline.hpp" #include "gc/g1/g1ConcurrentRefineSweepTask.hpp" #include "gc/g1/g1ConcurrentRefineThread.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp index 83a09c55a3f..5160d5ed036 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -22,7 +22,7 @@ * */ -#include "gc/g1/g1ConcurrentRefineStats.hpp" +#include "gc/g1/g1ConcurrentRefineStats.inline.hpp" #include "runtime/atomicAccess.hpp" #include "runtime/timer.hpp" @@ -39,19 +39,27 @@ G1ConcurrentRefineStats::G1ConcurrentRefineStats() : {} void G1ConcurrentRefineStats::add_atomic(G1ConcurrentRefineStats* other) { - AtomicAccess::add(&_sweep_duration, other->_sweep_duration, memory_order_relaxed); - AtomicAccess::add(&_yield_during_sweep_duration, other->_yield_during_sweep_duration, memory_order_relaxed); + _sweep_duration.add_then_fetch(other->_sweep_duration.load_relaxed(), memory_order_relaxed); + _yield_during_sweep_duration.add_then_fetch(other->yield_during_sweep_duration(), memory_order_relaxed); - AtomicAccess::add(&_cards_scanned, other->_cards_scanned, memory_order_relaxed); - AtomicAccess::add(&_cards_clean, other->_cards_clean, memory_order_relaxed); - AtomicAccess::add(&_cards_not_parsable, other->_cards_not_parsable, memory_order_relaxed); - AtomicAccess::add(&_cards_already_refer_to_cset, other->_cards_already_refer_to_cset, memory_order_relaxed); - AtomicAccess::add(&_cards_refer_to_cset, other->_cards_refer_to_cset, memory_order_relaxed); - AtomicAccess::add(&_cards_no_cross_region, other->_cards_no_cross_region, memory_order_relaxed); + _cards_scanned.add_then_fetch(other->cards_scanned(), memory_order_relaxed); + _cards_clean.add_then_fetch(other->cards_clean(), memory_order_relaxed); + _cards_not_parsable.add_then_fetch(other->cards_not_parsable(), memory_order_relaxed); + _cards_already_refer_to_cset.add_then_fetch(other->cards_already_refer_to_cset(), memory_order_relaxed); + _cards_refer_to_cset.add_then_fetch(other->cards_refer_to_cset(), memory_order_relaxed); + _cards_no_cross_region.add_then_fetch(other->cards_no_cross_region(), memory_order_relaxed); - AtomicAccess::add(&_refine_duration, other->_refine_duration, memory_order_relaxed); + _refine_duration.add_then_fetch(other->refine_duration(), memory_order_relaxed); } void G1ConcurrentRefineStats::reset() { - *this = G1ConcurrentRefineStats(); + _sweep_duration.store_relaxed(0); + _yield_during_sweep_duration.store_relaxed(0); + _cards_scanned.store_relaxed(0); + _cards_clean.store_relaxed(0); + _cards_not_parsable.store_relaxed(0); + _cards_already_refer_to_cset.store_relaxed(0); + _cards_refer_to_cset.store_relaxed(0); + _cards_no_cross_region.store_relaxed(0); + _refine_duration.store_relaxed(0); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.hpp index ce22f4317df..5f57c56ba6c 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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,61 +26,61 @@ #define SHARE_GC_G1_G1CONCURRENTREFINESTATS_HPP #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/ticks.hpp" // Collection of statistics for concurrent refinement processing. // Used for collecting per-thread statistics and for summaries over a // collection of threads. class G1ConcurrentRefineStats : public CHeapObj { - jlong _sweep_duration; // Time spent sweeping the table finding non-clean cards - // and refining them. - jlong _yield_during_sweep_duration; // Time spent yielding during the sweep (not doing the sweep). + Atomic _sweep_duration; // Time spent sweeping the table finding non-clean cards + // and refining them. + Atomic _yield_during_sweep_duration; // Time spent yielding during the sweep (not doing the sweep). - size_t _cards_scanned; // Total number of cards scanned. - size_t _cards_clean; // Number of cards found clean. - size_t _cards_not_parsable; // Number of cards we could not parse and left unrefined. - size_t _cards_already_refer_to_cset;// Number of cards marked found to be already young. - size_t _cards_refer_to_cset; // Number of dirty cards that were recently found to contain a to-cset reference. - size_t _cards_no_cross_region; // Number of dirty cards that were dirtied, but then cleaned again by the mutator. + Atomic _cards_scanned; // Total number of cards scanned. + Atomic _cards_clean; // Number of cards found clean. + Atomic _cards_not_parsable; // Number of cards we could not parse and left unrefined. + Atomic _cards_already_refer_to_cset;// Number of cards marked found to be already young. + Atomic _cards_refer_to_cset; // Number of dirty cards that were recently found to contain a to-cset reference. + Atomic _cards_no_cross_region; // Number of dirty cards that were dirtied, but then cleaned again by the mutator. - jlong _refine_duration; // Time spent during actual refinement. + Atomic _refine_duration; // Time spent during actual refinement. public: G1ConcurrentRefineStats(); // Time spent performing sweeping the refinement table (includes actual refinement, // but not yield time). - jlong sweep_duration() const { return _sweep_duration - _yield_during_sweep_duration; } - jlong yield_during_sweep_duration() const { return _yield_during_sweep_duration; } - jlong refine_duration() const { return _refine_duration; } + inline jlong sweep_duration() const; + inline jlong yield_during_sweep_duration() const; + inline jlong refine_duration() const; // Number of refined cards. - size_t refined_cards() const { return cards_not_clean(); } + inline size_t refined_cards() const; - size_t cards_scanned() const { return _cards_scanned; } - size_t cards_clean() const { return _cards_clean; } - size_t cards_not_clean() const { return _cards_scanned - _cards_clean; } - size_t cards_not_parsable() const { return _cards_not_parsable; } - size_t cards_already_refer_to_cset() const { return _cards_already_refer_to_cset; } - size_t cards_refer_to_cset() const { return _cards_refer_to_cset; } - size_t cards_no_cross_region() const { return _cards_no_cross_region; } + inline size_t cards_scanned() const; + inline size_t cards_clean() const; + inline size_t cards_not_clean() const; + inline size_t cards_not_parsable() const; + inline size_t cards_already_refer_to_cset() const; + inline size_t cards_refer_to_cset() const; + inline size_t cards_no_cross_region() const; // Number of cards that were marked dirty and in need of refinement. This includes cards recently // found to refer to the collection set as they originally were dirty. - size_t cards_pending() const { return cards_not_clean() - _cards_already_refer_to_cset; } + inline size_t cards_pending() const; - size_t cards_to_cset() const { return _cards_already_refer_to_cset + _cards_refer_to_cset; } + inline size_t cards_to_cset() const; - void inc_sweep_time(jlong t) { _sweep_duration += t; } - void inc_yield_during_sweep_duration(jlong t) { _yield_during_sweep_duration += t; } - void inc_refine_duration(jlong t) { _refine_duration += t; } + inline void inc_sweep_time(jlong t); + inline void inc_yield_during_sweep_duration(jlong t); + inline void inc_refine_duration(jlong t); - void inc_cards_scanned(size_t increment) { _cards_scanned += increment; } - void inc_cards_clean(size_t increment) { _cards_clean += increment; } - void inc_cards_not_parsable() { _cards_not_parsable++; } - void inc_cards_already_refer_to_cset() { _cards_already_refer_to_cset++; } - void inc_cards_refer_to_cset() { _cards_refer_to_cset++; } - void inc_cards_no_cross_region() { _cards_no_cross_region++; } + inline void inc_cards_scanned(size_t increment); + inline void inc_cards_clean(size_t increment); + inline void inc_cards_not_parsable(); + inline void inc_cards_already_refer_to_cset(); + inline void inc_cards_refer_to_cset(); + inline void inc_cards_no_cross_region(); void add_atomic(G1ConcurrentRefineStats* other); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.inline.hpp new file mode 100644 index 00000000000..e1a296c6494 --- /dev/null +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineStats.inline.hpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2026, 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. + * + * 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. + * + */ + +#ifndef SHARE_GC_G1_G1CONCURRENTREFINESTATS_INLINE_HPP +#define SHARE_GC_G1_G1CONCURRENTREFINESTATS_INLINE_HPP + +#include "gc/g1/g1ConcurrentRefineStats.hpp" + +inline jlong G1ConcurrentRefineStats::sweep_duration() const { + return _sweep_duration.load_relaxed() - yield_during_sweep_duration(); +} + +inline jlong G1ConcurrentRefineStats::yield_during_sweep_duration() const { + return _yield_during_sweep_duration.load_relaxed(); +} + +inline jlong G1ConcurrentRefineStats::refine_duration() const { + return _refine_duration.load_relaxed(); +} + +inline size_t G1ConcurrentRefineStats::refined_cards() const { + return cards_not_clean(); +} + +inline size_t G1ConcurrentRefineStats::cards_scanned() const { + return _cards_scanned.load_relaxed(); +} + +inline size_t G1ConcurrentRefineStats::cards_clean() const { + return _cards_clean.load_relaxed(); +} + +inline size_t G1ConcurrentRefineStats::cards_not_clean() const { + return cards_scanned() - cards_clean(); +} + +inline size_t G1ConcurrentRefineStats::cards_not_parsable() const { + return _cards_not_parsable.load_relaxed(); +} + +inline size_t G1ConcurrentRefineStats::cards_already_refer_to_cset() const { + return _cards_already_refer_to_cset.load_relaxed(); +} + +inline size_t G1ConcurrentRefineStats::cards_refer_to_cset() const { + return _cards_refer_to_cset.load_relaxed(); +} + +inline size_t G1ConcurrentRefineStats::cards_no_cross_region() const { + return _cards_no_cross_region.load_relaxed(); +} + +inline size_t G1ConcurrentRefineStats::cards_pending() const { + return cards_not_clean() - cards_already_refer_to_cset(); +} + +inline size_t G1ConcurrentRefineStats::cards_to_cset() const { + return cards_already_refer_to_cset() + cards_refer_to_cset(); +} + +inline void G1ConcurrentRefineStats::inc_sweep_time(jlong t) { + _sweep_duration.store_relaxed(_sweep_duration.load_relaxed() + t); +} + +inline void G1ConcurrentRefineStats::inc_yield_during_sweep_duration(jlong t) { + _yield_during_sweep_duration.store_relaxed(yield_during_sweep_duration() + t); +} + +inline void G1ConcurrentRefineStats::inc_refine_duration(jlong t) { + _refine_duration.store_relaxed(refine_duration() + t); +} + +inline void G1ConcurrentRefineStats::inc_cards_scanned(size_t increment) { + _cards_scanned.store_relaxed(cards_scanned() + increment); +} + +inline void G1ConcurrentRefineStats::inc_cards_clean(size_t increment) { + _cards_clean.store_relaxed(cards_clean() + increment); +} + +inline void G1ConcurrentRefineStats::inc_cards_not_parsable() { + _cards_not_parsable.store_relaxed(cards_not_parsable() + 1); +} + +inline void G1ConcurrentRefineStats::inc_cards_already_refer_to_cset() { + _cards_already_refer_to_cset.store_relaxed(cards_already_refer_to_cset() + 1); +} + +inline void G1ConcurrentRefineStats::inc_cards_refer_to_cset() { + _cards_refer_to_cset.store_relaxed(cards_refer_to_cset() + 1); +} + +inline void G1ConcurrentRefineStats::inc_cards_no_cross_region() { + _cards_no_cross_region.store_relaxed(cards_no_cross_region() + 1); +} + +#endif // SHARE_GC_G1_G1CONCURRENTREFINESTATS_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.cpp index ca5bc9ebe5f..ce944f2254d 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.cpp @@ -24,6 +24,7 @@ #include "gc/g1/g1CardTableClaimTable.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/g1ConcurrentRefineStats.inline.hpp" #include "gc/g1/g1ConcurrentRefineSweepTask.hpp" class G1RefineRegionClosure : public G1HeapRegionClosure { diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.hpp index bf24c5ae850..827b9a3c402 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineSweepTask.hpp @@ -25,10 +25,10 @@ #ifndef SHARE_GC_G1_G1CONCURRENTREFINESWEEPTASK_HPP #define SHARE_GC_G1_G1CONCURRENTREFINESWEEPTASK_HPP -#include "gc/g1/g1ConcurrentRefineStats.hpp" #include "gc/shared/workerThread.hpp" class G1CardTableClaimTable; +class G1ConcurrentRefineStats; class G1ConcurrentRefineSweepTask : public WorkerTask { G1CardTableClaimTable* _scan_state; diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp index eccfe466d48..6b51e5eef62 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp @@ -26,7 +26,7 @@ #include "gc/g1/g1CardTableClaimTable.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" -#include "gc/g1/g1ConcurrentRefineStats.hpp" +#include "gc/g1/g1ConcurrentRefineStats.inline.hpp" #include "gc/g1/g1ConcurrentRefineSweepTask.hpp" #include "gc/g1/g1ConcurrentRefineThread.hpp" #include "gc/shared/gcTraceTime.inline.hpp" diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp index 7cdc001d348..2ecbdc668eb 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp @@ -25,7 +25,6 @@ #ifndef SHARE_GC_G1_G1CONCURRENTREFINETHREAD_HPP #define SHARE_GC_G1_G1CONCURRENTREFINETHREAD_HPP -#include "gc/g1/g1ConcurrentRefineStats.hpp" #include "gc/shared/concurrentGCThread.hpp" #include "runtime/mutex.hpp" #include "utilities/globalDefinitions.hpp" diff --git a/src/hotspot/share/gc/g1/g1EvacStats.cpp b/src/hotspot/share/gc/g1/g1EvacStats.cpp index 049175a4ecc..1d54b184e64 100644 --- a/src/hotspot/share/gc/g1/g1EvacStats.cpp +++ b/src/hotspot/share/gc/g1/g1EvacStats.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -22,13 +22,24 @@ * */ -#include "gc/g1/g1EvacStats.hpp" +#include "gc/g1/g1EvacStats.inline.hpp" #include "gc/shared/gc_globals.hpp" #include "gc/shared/gcId.hpp" #include "logging/log.hpp" #include "memory/allocation.inline.hpp" #include "runtime/globals.hpp" +void G1EvacStats::reset() { + PLABStats::reset(); + _region_end_waste.store_relaxed(0); + _regions_filled.store_relaxed(0); + _num_plab_filled.store_relaxed(0); + _direct_allocated.store_relaxed(0); + _num_direct_allocated.store_relaxed(0); + _failure_used.store_relaxed(0); + _failure_waste.store_relaxed(0); +} + void G1EvacStats::log_plab_allocation() { log_debug(gc, plab)("%s PLAB allocation: " "allocated: %zuB, " @@ -51,13 +62,13 @@ void G1EvacStats::log_plab_allocation() { "failure used: %zuB, " "failure wasted: %zuB", _description, - _region_end_waste * HeapWordSize, - _regions_filled, - _num_plab_filled, - _direct_allocated * HeapWordSize, - _num_direct_allocated, - _failure_used * HeapWordSize, - _failure_waste * HeapWordSize); + region_end_waste() * HeapWordSize, + regions_filled(), + num_plab_filled(), + direct_allocated() * HeapWordSize, + num_direct_allocated(), + failure_used() * HeapWordSize, + failure_waste() * HeapWordSize); } void G1EvacStats::log_sizing(size_t calculated_words, size_t net_desired_words) { @@ -109,7 +120,7 @@ size_t G1EvacStats::compute_desired_plab_size() const { // threads do not allocate anything but a few rather large objects. In this // degenerate case the PLAB size would simply quickly tend to minimum PLAB size, // which is an okay reaction. - size_t const used_for_waste_calculation = used() > _region_end_waste ? used() - _region_end_waste : 0; + size_t const used_for_waste_calculation = used() > region_end_waste() ? used() - region_end_waste() : 0; size_t const total_waste_allowed = used_for_waste_calculation * TargetPLABWastePct; return (size_t)((double)total_waste_allowed / (100 - G1LastPLABAverageOccupancy)); diff --git a/src/hotspot/share/gc/g1/g1EvacStats.hpp b/src/hotspot/share/gc/g1/g1EvacStats.hpp index e6eb80442d6..b250d4580b5 100644 --- a/src/hotspot/share/gc/g1/g1EvacStats.hpp +++ b/src/hotspot/share/gc/g1/g1EvacStats.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -27,6 +27,7 @@ #include "gc/shared/gcUtil.hpp" #include "gc/shared/plab.hpp" +#include "runtime/atomic.hpp" // Records various memory allocation statistics gathered during evacuation. All sizes // are in HeapWords. @@ -36,30 +37,21 @@ class G1EvacStats : public PLABStats { AdaptiveWeightedAverage _net_plab_size_filter; // Integrator with decay - size_t _region_end_waste; // Number of words wasted due to skipping to the next region. - uint _regions_filled; // Number of regions filled completely. - size_t _num_plab_filled; // Number of PLABs filled and retired. - size_t _direct_allocated; // Number of words allocated directly into the regions. - size_t _num_direct_allocated; // Number of direct allocation attempts. + Atomic _region_end_waste; // Number of words wasted due to skipping to the next region. + Atomic _regions_filled; // Number of regions filled completely. + Atomic _num_plab_filled; // Number of PLABs filled and retired. + Atomic _direct_allocated; // Number of words allocated directly into the regions. + Atomic _num_direct_allocated; // Number of direct allocation attempts. // Number of words in live objects remaining in regions that ultimately suffered an // evacuation failure. This is used in the regions when the regions are made old regions. - size_t _failure_used; + Atomic _failure_used; // Number of words wasted in regions which failed evacuation. This is the sum of space // for objects successfully copied out of the regions (now dead space) plus waste at the // end of regions. - size_t _failure_waste; + Atomic _failure_waste; - virtual void reset() { - PLABStats::reset(); - _region_end_waste = 0; - _regions_filled = 0; - _num_plab_filled = 0; - _direct_allocated = 0; - _num_direct_allocated = 0; - _failure_used = 0; - _failure_waste = 0; - } + virtual void reset(); void log_plab_allocation(); void log_sizing(size_t calculated_words, size_t net_desired_words); @@ -77,16 +69,16 @@ public: // Should be called at the end of a GC pause. void adjust_desired_plab_size(); - uint regions_filled() const { return _regions_filled; } - size_t num_plab_filled() const { return _num_plab_filled; } - size_t region_end_waste() const { return _region_end_waste; } - size_t direct_allocated() const { return _direct_allocated; } - size_t num_direct_allocated() const { return _num_direct_allocated; } + uint regions_filled() const; + size_t num_plab_filled() const; + size_t region_end_waste() const; + size_t direct_allocated() const; + size_t num_direct_allocated() const; // Amount of space in heapwords used in the failing regions when an evacuation failure happens. - size_t failure_used() const { return _failure_used; } + size_t failure_used() const; // Amount of space in heapwords wasted (unused) in the failing regions when an evacuation failure happens. - size_t failure_waste() const { return _failure_waste; } + size_t failure_waste() const; inline void add_num_plab_filled(size_t value); inline void add_direct_allocated(size_t value); diff --git a/src/hotspot/share/gc/g1/g1EvacStats.inline.hpp b/src/hotspot/share/gc/g1/g1EvacStats.inline.hpp index c90598a30cb..2bd3b37719a 100644 --- a/src/hotspot/share/gc/g1/g1EvacStats.inline.hpp +++ b/src/hotspot/share/gc/g1/g1EvacStats.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -27,28 +27,54 @@ #include "gc/g1/g1EvacStats.hpp" -#include "runtime/atomicAccess.hpp" +inline uint G1EvacStats::regions_filled() const { + return _regions_filled.load_relaxed(); +} + +inline size_t G1EvacStats::num_plab_filled() const { + return _num_plab_filled.load_relaxed(); +} + +inline size_t G1EvacStats::region_end_waste() const { + return _region_end_waste.load_relaxed(); +} + +inline size_t G1EvacStats::direct_allocated() const { + return _direct_allocated.load_relaxed(); +} + +inline size_t G1EvacStats::num_direct_allocated() const { + return _num_direct_allocated.load_relaxed(); +} + +inline size_t G1EvacStats::failure_used() const { + return _failure_used.load_relaxed(); +} + +inline size_t G1EvacStats::failure_waste() const { + return _failure_waste.load_relaxed(); +} inline void G1EvacStats::add_direct_allocated(size_t value) { - AtomicAccess::add(&_direct_allocated, value, memory_order_relaxed); + _direct_allocated.add_then_fetch(value, memory_order_relaxed); } inline void G1EvacStats::add_num_plab_filled(size_t value) { - AtomicAccess::add(&_num_plab_filled, value, memory_order_relaxed); + _num_plab_filled.add_then_fetch(value, memory_order_relaxed); } inline void G1EvacStats::add_num_direct_allocated(size_t value) { - AtomicAccess::add(&_num_direct_allocated, value, memory_order_relaxed); + _num_direct_allocated.add_then_fetch(value, memory_order_relaxed); } inline void G1EvacStats::add_region_end_waste(size_t value) { - AtomicAccess::add(&_region_end_waste, value, memory_order_relaxed); - AtomicAccess::inc(&_regions_filled, memory_order_relaxed); + _region_end_waste.add_then_fetch(value, memory_order_relaxed); + _regions_filled.add_then_fetch(1u, memory_order_relaxed); } inline void G1EvacStats::add_failure_used_and_waste(size_t used, size_t waste) { - AtomicAccess::add(&_failure_used, used, memory_order_relaxed); - AtomicAccess::add(&_failure_waste, waste, memory_order_relaxed); + _failure_used.add_then_fetch(used, memory_order_relaxed); + _failure_waste.add_then_fetch(waste, memory_order_relaxed); } #endif // SHARE_GC_G1_G1EVACSTATS_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index 7395df01760..6c8cc7028cc 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -134,10 +134,10 @@ G1FullCollector::G1FullCollector(G1CollectedHeap* heap, _compaction_points = NEW_C_HEAP_ARRAY(G1FullGCCompactionPoint*, _num_workers, mtGC); _live_stats = NEW_C_HEAP_ARRAY(G1RegionMarkStats, _heap->max_num_regions(), mtGC); - _compaction_tops = NEW_C_HEAP_ARRAY(HeapWord*, _heap->max_num_regions(), mtGC); + _compaction_tops = NEW_C_HEAP_ARRAY(Atomic, _heap->max_num_regions(), mtGC); for (uint j = 0; j < heap->max_num_regions(); j++) { _live_stats[j].clear(); - _compaction_tops[j] = nullptr; + ::new (&_compaction_tops[j]) Atomic{}; } _partial_array_state_manager = new PartialArrayStateManager(_num_workers); @@ -167,7 +167,7 @@ G1FullCollector::~G1FullCollector() { FREE_C_HEAP_ARRAY(G1FullGCMarker*, _markers); FREE_C_HEAP_ARRAY(G1FullGCCompactionPoint*, _compaction_points); - FREE_C_HEAP_ARRAY(HeapWord*, _compaction_tops); + FREE_C_HEAP_ARRAY(Atomic, _compaction_tops); FREE_C_HEAP_ARRAY(G1RegionMarkStats, _live_stats); } diff --git a/src/hotspot/share/gc/g1/g1FullCollector.hpp b/src/hotspot/share/gc/g1/g1FullCollector.hpp index 28ecffad944..7e455b07013 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.hpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.hpp @@ -96,7 +96,7 @@ class G1FullCollector : StackObj { G1FullGCHeapRegionAttr _region_attr_table; - HeapWord* volatile* _compaction_tops; + Atomic* _compaction_tops; public: G1FullCollector(G1CollectedHeap* heap, diff --git a/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp b/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp index b52f3d79604..0c201f0e43f 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -63,11 +63,11 @@ void G1FullCollector::update_from_skip_compacting_to_compacting(uint region_idx) } void G1FullCollector::set_compaction_top(G1HeapRegion* r, HeapWord* value) { - AtomicAccess::store(&_compaction_tops[r->hrm_index()], value); + _compaction_tops[r->hrm_index()].store_relaxed(value); } HeapWord* G1FullCollector::compaction_top(G1HeapRegion* r) const { - return AtomicAccess::load(&_compaction_tops[r->hrm_index()]); + return _compaction_tops[r->hrm_index()].load_relaxed(); } void G1FullCollector::set_has_compaction_targets() { diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.cpp b/src/hotspot/share/gc/g1/g1HeapRegion.cpp index 361e19d4be5..2052a3ce156 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -44,7 +44,7 @@ #include "oops/access.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/globals_extension.hpp" #include "utilities/powerOfTwo.hpp" @@ -131,8 +131,8 @@ void G1HeapRegion::hr_clear(bool clear_space) { G1CollectedHeap::heap()->concurrent_mark()->reset_top_at_mark_start(this); - _parsable_bottom = bottom(); - _garbage_bytes = 0; + _parsable_bottom.store_relaxed(bottom()); + _garbage_bytes.store_relaxed(0); _incoming_refs = 0; if (clear_space) clear(SpaceDecorator::Mangle); @@ -294,12 +294,12 @@ void G1HeapRegion::report_region_type_change(G1HeapRegionTraceType::Type to) { // young gen regions never have their PB set to anything other than bottom. assert(parsable_bottom_acquire() == bottom(), "must be"); - _garbage_bytes = 0; + _garbage_bytes.store_relaxed(0); _incoming_refs = 0; } void G1HeapRegion::note_self_forward_chunk_done(size_t garbage_bytes) { - AtomicAccess::add(&_garbage_bytes, garbage_bytes, memory_order_relaxed); + _garbage_bytes.add_then_fetch(garbage_bytes, memory_order_relaxed); } // Code roots support @@ -448,7 +448,7 @@ void G1HeapRegion::print_on(outputStream* st) const { st->print("|-"); } } - st->print("|%3zu", AtomicAccess::load(&_pinned_object_count)); + st->print("|%3zu", _pinned_object_count.load_relaxed()); st->print_cr(""); } diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.hpp b/src/hotspot/share/gc/g1/g1HeapRegion.hpp index fe915b0dafe..2b4b640d52b 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -33,6 +33,7 @@ #include "gc/shared/ageTable.hpp" #include "gc/shared/spaceDecorator.hpp" #include "gc/shared/verifyOption.hpp" +#include "runtime/atomic.hpp" #include "runtime/mutex.hpp" #include "utilities/macros.hpp" @@ -73,7 +74,7 @@ class G1HeapRegion : public CHeapObj { HeapWord* const _bottom; HeapWord* const _end; - HeapWord* volatile _top; + Atomic _top; G1BlockOffsetTable* _bot; @@ -89,8 +90,8 @@ public: HeapWord* bottom() const { return _bottom; } HeapWord* end() const { return _end; } - void set_top(HeapWord* value) { _top = value; } - HeapWord* top() const { return _top; } + void set_top(HeapWord* value) { _top.store_relaxed(value); } + HeapWord* top() const { return _top.load_relaxed(); } // See the comment above in the declaration of _pre_dummy_top for an // explanation of what it is. @@ -231,10 +232,10 @@ private: // // Below this limit the marking bitmap must be used to determine size and // liveness. - HeapWord* volatile _parsable_bottom; + Atomic _parsable_bottom; // Amount of dead data in the region. - size_t _garbage_bytes; + Atomic _garbage_bytes; // Approximate number of references to this regions at the end of concurrent // marking. We we do not mark through all objects, so this is an estimate. @@ -249,7 +250,7 @@ private: uint _node_index; // Number of objects in this region that are currently pinned. - volatile size_t _pinned_object_count; + Atomic _pinned_object_count; void report_region_type_change(G1HeapRegionTraceType::Type to); @@ -331,7 +332,7 @@ public: } // A lower bound on the amount of garbage bytes in the region. - size_t garbage_bytes() const { return _garbage_bytes; } + size_t garbage_bytes() const { return _garbage_bytes.load_relaxed(); } // Return the amount of bytes we'll reclaim if we collect this // region. This includes not only the known garbage bytes in the @@ -393,8 +394,8 @@ public: bool is_old_or_humongous() const { return _type.is_old_or_humongous(); } - size_t pinned_count() const { return AtomicAccess::load(&_pinned_object_count); } - bool has_pinned_objects() const { return pinned_count() > 0; } + inline size_t pinned_count() const; + inline bool has_pinned_objects() const; void set_free(); diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp b/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp index f25bf62c9be..4f242b7a537 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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,6 @@ #include "gc/g1/g1Policy.hpp" #include "gc/g1/g1Predictions.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/init.hpp" #include "runtime/prefetch.inline.hpp" #include "runtime/safepoint.hpp" @@ -131,7 +130,7 @@ inline void G1HeapRegion::prepare_for_full_gc() { // After marking and class unloading the heap temporarily contains dead objects // with unloaded klasses. Moving parsable_bottom makes some (debug) code correctly // skip dead objects. - _parsable_bottom = top(); + _parsable_bottom.store_relaxed(top()); } inline void G1HeapRegion::reset_compacted_after_full_gc(HeapWord* new_top) { @@ -154,7 +153,7 @@ inline void G1HeapRegion::reset_after_full_gc_common() { // Everything above bottom() is parsable and live. reset_parsable_bottom(); - _garbage_bytes = 0; + _garbage_bytes.store_relaxed(0); _incoming_refs = 0; @@ -188,20 +187,22 @@ inline void G1HeapRegion::apply_to_marked_objects(G1CMBitMap* bitmap, ApplyToMar inline HeapWord* G1HeapRegion::par_allocate(size_t min_word_size, size_t desired_word_size, size_t* actual_word_size) { + HeapWord* obj = top(); do { - HeapWord* obj = top(); size_t available = pointer_delta(end(), obj); size_t want_to_allocate = MIN2(available, desired_word_size); if (want_to_allocate >= min_word_size) { HeapWord* new_top = obj + want_to_allocate; - HeapWord* result = AtomicAccess::cmpxchg(&_top, obj, new_top); - // result can be one of two: - // the old top value: the exchange succeeded + HeapWord* result = _top.compare_exchange(obj, new_top); + // Result can be one of two: + // the old top value: the exchange succeeded, return. // otherwise: the new value of the top is returned. if (result == obj) { assert(is_object_aligned(obj) && is_object_aligned(new_top), "checking alignment"); *actual_word_size = want_to_allocate; return obj; + } else { + obj = result; } } else { return nullptr; @@ -254,27 +255,27 @@ inline void G1HeapRegion::update_bot_for_block(HeapWord* start, HeapWord* end) { inline HeapWord* G1HeapRegion::parsable_bottom() const { assert(!is_init_completed() || SafepointSynchronize::is_at_safepoint(), "only during initialization or safepoint"); - return _parsable_bottom; + return _parsable_bottom.load_relaxed(); } inline HeapWord* G1HeapRegion::parsable_bottom_acquire() const { - return AtomicAccess::load_acquire(&_parsable_bottom); + return _parsable_bottom.load_acquire(); } inline void G1HeapRegion::reset_parsable_bottom() { - AtomicAccess::release_store(&_parsable_bottom, bottom()); + _parsable_bottom.release_store(bottom()); } inline void G1HeapRegion::note_end_of_marking(HeapWord* top_at_mark_start, size_t marked_bytes, size_t incoming_refs) { assert_at_safepoint(); if (top_at_mark_start != bottom()) { - _garbage_bytes = byte_size(bottom(), top_at_mark_start) - marked_bytes; + _garbage_bytes.store_relaxed(byte_size(bottom(), top_at_mark_start) - marked_bytes); _incoming_refs = incoming_refs; } if (needs_scrubbing()) { - _parsable_bottom = top_at_mark_start; + _parsable_bottom.store_relaxed(top_at_mark_start); } } @@ -286,6 +287,14 @@ inline bool G1HeapRegion::needs_scrubbing() const { return is_old(); } +inline size_t G1HeapRegion::pinned_count() const { + return _pinned_object_count.load_relaxed(); +} + +inline bool G1HeapRegion::has_pinned_objects() const { + return pinned_count() > 0; +} + inline bool G1HeapRegion::in_collection_set() const { return G1CollectedHeap::heap()->is_in_cset(this); } @@ -511,7 +520,7 @@ inline void G1HeapRegion::record_surv_words_in_group(size_t words_survived) { inline void G1HeapRegion::add_pinned_object_count(size_t value) { assert(value != 0, "wasted effort"); assert(!is_free(), "trying to pin free region %u, adding %zu", hrm_index(), value); - AtomicAccess::add(&_pinned_object_count, value, memory_order_relaxed); + _pinned_object_count.add_then_fetch(value, memory_order_relaxed); } inline void G1HeapRegion::install_cset_group(G1CSetCandidateGroup* cset_group) { diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp index 795b6543bae..44897c8a277 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -713,8 +713,10 @@ void G1HeapRegionManager::verify_optional() { G1HeapRegionClaimer::G1HeapRegionClaimer(uint n_workers) : _n_workers(n_workers), _n_regions(G1CollectedHeap::heap()->_hrm._next_highest_used_hrm_index), _claims(nullptr) { - uint* new_claims = NEW_C_HEAP_ARRAY(uint, _n_regions, mtGC); - memset(new_claims, Unclaimed, sizeof(*_claims) * _n_regions); + Atomic* new_claims = NEW_C_HEAP_ARRAY(Atomic, _n_regions, mtGC); + for (uint i = 0; i < _n_regions; i++) { + new_claims[i].store_relaxed(Unclaimed); + } _claims = new_claims; } @@ -730,13 +732,12 @@ uint G1HeapRegionClaimer::offset_for_worker(uint worker_id) const { bool G1HeapRegionClaimer::is_region_claimed(uint region_index) const { assert(region_index < _n_regions, "Invalid index."); - return _claims[region_index] == Claimed; + return _claims[region_index].load_relaxed() == Claimed; } bool G1HeapRegionClaimer::claim_region(uint region_index) { assert(region_index < _n_regions, "Invalid index."); - uint old_val = AtomicAccess::cmpxchg(&_claims[region_index], Unclaimed, Claimed); - return old_val == Unclaimed; + return _claims[region_index].compare_set(Unclaimed, Claimed); } class G1RebuildFreeListTask : public WorkerTask { diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp index b4ce3b0a8be..eb593ff408e 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 "gc/g1/g1HeapRegionSet.hpp" #include "gc/g1/g1RegionToSpaceMapper.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "services/memoryUsage.hpp" class G1HeapRegion; @@ -294,7 +295,7 @@ public: class G1HeapRegionClaimer : public StackObj { uint _n_workers; uint _n_regions; - volatile uint* _claims; + Atomic* _claims; static const uint Unclaimed = 0; static const uint Claimed = 1; diff --git a/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp b/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp index 50002ac2bfe..f280d76f3c7 100644 --- a/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp +++ b/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp @@ -39,7 +39,7 @@ bool G1PeriodicGCTask::should_start_periodic_gc(G1CollectedHeap* g1h, SuspendibleThreadSetJoiner sts; // If we are currently in a concurrent mark we are going to uncommit memory soon. - if (g1h->concurrent_mark()->cm_thread()->in_progress()) { + if (g1h->concurrent_mark()->in_progress()) { log_debug(gc, periodic)("Concurrent cycle in progress. Skipping."); return false; } diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 8818b477aae..98e6acc1d77 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -32,7 +32,7 @@ #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/g1ConcurrentRefine.hpp" -#include "gc/g1/g1ConcurrentRefineStats.hpp" +#include "gc/g1/g1ConcurrentRefineStats.inline.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionRemSet.inline.hpp" @@ -739,7 +739,7 @@ double G1Policy::constant_other_time_ms(double pause_time_ms) const { } bool G1Policy::about_to_start_mixed_phase() const { - return _g1h->concurrent_mark()->cm_thread()->in_progress() || collector_state()->in_young_gc_before_mixed(); + return _g1h->concurrent_mark()->in_progress() || collector_state()->in_young_gc_before_mixed(); } bool G1Policy::need_to_start_conc_mark(const char* source, size_t allocation_word_size) { @@ -1235,7 +1235,7 @@ bool G1Policy::force_concurrent_start_if_outside_cycle(GCCause::Cause gc_cause) // We actually check whether we are marking here and not if we are in a // reclamation phase. This means that we will schedule a concurrent mark // even while we are still in the process of reclaiming memory. - bool during_cycle = _g1h->concurrent_mark()->cm_thread()->in_progress(); + bool during_cycle = _g1h->concurrent_mark()->in_progress(); if (!during_cycle) { log_debug(gc, ergo)("Request concurrent cycle initiation (requested by GC cause). " "GC cause: %s", diff --git a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp index d9b7ec294bd..c5f55e1d20c 100644 --- a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp +++ b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp @@ -29,12 +29,12 @@ G1RegionMarkStatsCache::G1RegionMarkStatsCache(G1RegionMarkStats* target, uint num_cache_entries) : _target(target), + _cache(NEW_C_HEAP_ARRAY(G1RegionMarkStatsCacheEntry, num_cache_entries, mtGC)), _num_cache_entries(num_cache_entries), _num_cache_entries_mask(_num_cache_entries - 1) { guarantee(is_power_of_2(num_cache_entries), "Number of cache entries must be power of two, but is %u", num_cache_entries); - _cache = NEW_C_HEAP_ARRAY(G1RegionMarkStatsCacheEntry, _num_cache_entries, mtGC); } G1RegionMarkStatsCache::~G1RegionMarkStatsCache() { diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index c7724de280f..0c9a0fad8f2 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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,7 +50,7 @@ #include "memory/resourceArea.hpp" #include "oops/access.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/os.hpp" #include "utilities/align.hpp" #include "utilities/globalDefinitions.hpp" @@ -107,46 +107,48 @@ class G1RemSetScanState : public CHeapObj { // Set of (unique) regions that can be added to concurrently. class G1DirtyRegions : public CHeapObj { uint* _buffer; - uint _cur_idx; + Atomic _cur_idx; size_t _max_reserved_regions; - bool* _contains; + Atomic* _contains; public: G1DirtyRegions(size_t max_reserved_regions) : _buffer(NEW_C_HEAP_ARRAY(uint, max_reserved_regions, mtGC)), _cur_idx(0), _max_reserved_regions(max_reserved_regions), - _contains(NEW_C_HEAP_ARRAY(bool, max_reserved_regions, mtGC)) { + _contains(NEW_C_HEAP_ARRAY(Atomic, max_reserved_regions, mtGC)) { reset(); } ~G1DirtyRegions() { FREE_C_HEAP_ARRAY(uint, _buffer); - FREE_C_HEAP_ARRAY(bool, _contains); + FREE_C_HEAP_ARRAY(Atomic, _contains); } void reset() { - _cur_idx = 0; - ::memset(_contains, false, _max_reserved_regions * sizeof(bool)); + _cur_idx.store_relaxed(0); + for (uint i = 0; i < _max_reserved_regions; i++) { + _contains[i].store_relaxed(false); + } } - uint size() const { return _cur_idx; } + uint size() const { return _cur_idx.load_relaxed(); } uint at(uint idx) const { - assert(idx < _cur_idx, "Index %u beyond valid regions", idx); + assert(idx < size(), "Index %u beyond valid regions", idx); return _buffer[idx]; } void add_dirty_region(uint region) { - if (_contains[region]) { + if (_contains[region].load_relaxed()) { return; } - bool marked_as_dirty = AtomicAccess::cmpxchg(&_contains[region], false, true) == false; + bool marked_as_dirty = _contains[region].compare_set(false, true); if (marked_as_dirty) { - uint allocated = AtomicAccess::fetch_then_add(&_cur_idx, 1u); + uint allocated = _cur_idx.fetch_then_add(1u); _buffer[allocated] = region; } } @@ -155,9 +157,11 @@ class G1RemSetScanState : public CHeapObj { void merge(const G1DirtyRegions* other) { for (uint i = 0; i < other->size(); i++) { uint region = other->at(i); - if (!_contains[region]) { - _buffer[_cur_idx++] = region; - _contains[region] = true; + if (!_contains[region].load_relaxed()) { + uint cur = _cur_idx.load_relaxed(); + _buffer[cur] = region; + _cur_idx.store_relaxed(cur + 1); + _contains[region].store_relaxed(true); } } } @@ -173,7 +177,7 @@ class G1RemSetScanState : public CHeapObj { class G1ClearCardTableTask : public G1AbstractSubTask { G1CollectedHeap* _g1h; G1DirtyRegions* _regions; - uint volatile _cur_dirty_regions; + Atomic _cur_dirty_regions; G1RemSetScanState* _scan_state; @@ -210,8 +214,9 @@ class G1ClearCardTableTask : public G1AbstractSubTask { void do_work(uint worker_id) override { const uint num_regions_per_worker = num_cards_per_worker / (uint)G1HeapRegion::CardsPerRegion; - while (_cur_dirty_regions < _regions->size()) { - uint next = AtomicAccess::fetch_then_add(&_cur_dirty_regions, num_regions_per_worker); + uint cur = _cur_dirty_regions.load_relaxed(); + while (cur < _regions->size()) { + uint next = _cur_dirty_regions.fetch_then_add(num_regions_per_worker); uint max = MIN2(next + num_regions_per_worker, _regions->size()); for (uint i = next; i < max; i++) { @@ -226,6 +231,7 @@ class G1ClearCardTableTask : public G1AbstractSubTask { // old regions use it for old->collection set candidates, so they should not be cleared // either. } + cur = max; } } }; @@ -1115,7 +1121,7 @@ class G1MergeHeapRootsTask : public WorkerTask { bool _initial_evacuation; - volatile bool _fast_reclaim_handled; + Atomic _fast_reclaim_handled; public: G1MergeHeapRootsTask(G1RemSetScanState* scan_state, uint num_workers, bool initial_evacuation) : @@ -1143,8 +1149,8 @@ public: // 1. eager-reclaim candidates if (_initial_evacuation && g1h->has_humongous_reclaim_candidates() && - !_fast_reclaim_handled && - !AtomicAccess::cmpxchg(&_fast_reclaim_handled, false, true)) { + !_fast_reclaim_handled.load_relaxed() && + _fast_reclaim_handled.compare_set(false, true)) { G1GCParPhaseTimesTracker subphase_x(p, G1GCPhaseTimes::MergeER, worker_id); diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp index 1c024f2943b..56ab3a4b0fe 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.cpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp @@ -85,7 +85,7 @@ void VM_G1TryInitiateConcMark::doit() { GCCauseSetter x(g1h, _gc_cause); _mark_in_progress = g1h->collector_state()->mark_in_progress(); - _cycle_already_in_progress = g1h->concurrent_mark()->cm_thread()->in_progress(); + _cycle_already_in_progress = g1h->concurrent_mark()->in_progress(); if (!g1h->policy()->force_concurrent_start_if_outside_cycle(_gc_cause)) { // Failure to force the next GC pause to be a concurrent start indicates diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index 36cc44a8b7c..a9db9a7c269 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -36,6 +36,7 @@ #include "gc/g1/g1EvacFailureRegions.inline.hpp" #include "gc/g1/g1EvacInfo.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" +#include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionPrinter.hpp" #include "gc/g1/g1MonitoringSupport.hpp" #include "gc/g1/g1ParScanThreadState.inline.hpp" diff --git a/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp index b11213ddeb3..c0870b7a726 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPreEvacuateTasks.cpp @@ -23,7 +23,6 @@ */ #include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1ConcurrentRefineStats.hpp" #include "gc/g1/g1RegionPinCache.inline.hpp" #include "gc/g1/g1ThreadLocalData.hpp" #include "gc/g1/g1YoungGCPreEvacuateTasks.hpp" diff --git a/src/hotspot/share/gc/g1/vmStructs_g1.hpp b/src/hotspot/share/gc/g1/vmStructs_g1.hpp index 21c86d47a6b..af236ec8581 100644 --- a/src/hotspot/share/gc/g1/vmStructs_g1.hpp +++ b/src/hotspot/share/gc/g1/vmStructs_g1.hpp @@ -28,6 +28,7 @@ #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/g1HeapRegion.hpp" #include "gc/g1/g1HeapRegionManager.hpp" +#include "runtime/atomic.hpp" #include "utilities/macros.hpp" #define VM_STRUCTS_G1GC(nonstatic_field, \ @@ -39,9 +40,9 @@ \ nonstatic_field(G1HeapRegion, _type, G1HeapRegionType) \ nonstatic_field(G1HeapRegion, _bottom, HeapWord* const) \ - nonstatic_field(G1HeapRegion, _top, HeapWord* volatile) \ + nonstatic_field(G1HeapRegion, _top, Atomic) \ nonstatic_field(G1HeapRegion, _end, HeapWord* const) \ - volatile_nonstatic_field(G1HeapRegion, _pinned_object_count, size_t) \ + volatile_nonstatic_field(G1HeapRegion, _pinned_object_count, Atomic)\ \ nonstatic_field(G1HeapRegionType, _tag, G1HeapRegionType::Tag volatile) \ \ diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp index e0b1edf2efc..c5d112ffbc1 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2026, 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,7 +31,7 @@ #include "memory/allocation.inline.hpp" #include "oops/oop.inline.hpp" #include "oops/typeArrayOop.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/java.hpp" #include "runtime/javaThread.hpp" #include "runtime/os.inline.hpp" @@ -489,7 +489,7 @@ HeapWord* MutableNUMASpace::cas_allocate(size_t size) { if (p != nullptr) { HeapWord* cur_top, *cur_chunk_top = p + size; while ((cur_top = top()) < cur_chunk_top) { // Keep _top updated. - if (AtomicAccess::cmpxchg(top_addr(), cur_top, cur_chunk_top) == cur_top) { + if (top_addr()->compare_set(cur_top, cur_chunk_top)) { break; } } diff --git a/src/hotspot/share/gc/parallel/mutableSpace.cpp b/src/hotspot/share/gc/parallel/mutableSpace.cpp index fc42fc1eab2..d99db493989 100644 --- a/src/hotspot/share/gc/parallel/mutableSpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableSpace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -28,7 +28,6 @@ #include "memory/iterator.inline.hpp" #include "memory/universe.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/javaThread.hpp" #include "runtime/safepoint.hpp" #include "utilities/align.hpp" @@ -123,7 +122,7 @@ void MutableSpace::initialize(MemRegion mr, // makes the new space available for allocation by other threads. So this // assignment must follow all other configuration and initialization that // might be done for expansion. - AtomicAccess::release_store(end_addr(), mr.end()); + _end.release_store(mr.end()); if (clear_space) { clear(mangle_space); @@ -140,7 +139,7 @@ void MutableSpace::clear(bool mangle_space) { #ifndef PRODUCT void MutableSpace::mangle_unused_area() { - mangle_region(MemRegion(_top, _end)); + mangle_region(MemRegion(top(), end())); } void MutableSpace::mangle_region(MemRegion mr) { @@ -155,14 +154,10 @@ HeapWord* MutableSpace::cas_allocate(size_t size) { // If end is read first, other threads may advance end and top such that // current top > old end and current top + size > current end. Then // pointer_delta underflows, allowing installation of top > current end. - HeapWord* obj = AtomicAccess::load_acquire(top_addr()); + HeapWord* obj = _top.load_acquire(); if (pointer_delta(end(), obj) >= size) { HeapWord* new_top = obj + size; - HeapWord* result = AtomicAccess::cmpxchg(top_addr(), obj, new_top); - // result can be one of two: - // the old top value: the exchange succeeded - // otherwise: the new value of the top is returned. - if (result != obj) { + if (!_top.compare_set(obj, new_top)) { continue; // another thread beat us to the allocation, try again } assert(is_object_aligned(obj) && is_object_aligned(new_top), @@ -177,7 +172,7 @@ HeapWord* MutableSpace::cas_allocate(size_t size) { // Try to deallocate previous allocation. Returns true upon success. bool MutableSpace::cas_deallocate(HeapWord *obj, size_t size) { HeapWord* expected_top = obj + size; - return AtomicAccess::cmpxchg(top_addr(), expected_top, obj) == expected_top; + return _top.compare_set(expected_top, obj); } void MutableSpace::oop_iterate(OopIterateClosure* cl) { diff --git a/src/hotspot/share/gc/parallel/mutableSpace.hpp b/src/hotspot/share/gc/parallel/mutableSpace.hpp index 9d3894e2489..28df19a7c4b 100644 --- a/src/hotspot/share/gc/parallel/mutableSpace.hpp +++ b/src/hotspot/share/gc/parallel/mutableSpace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -28,6 +28,7 @@ #include "memory/allocation.hpp" #include "memory/iterator.hpp" #include "memory/memRegion.hpp" +#include "runtime/atomic.hpp" #include "utilities/copy.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" @@ -53,8 +54,8 @@ class MutableSpace: public CHeapObj { MemRegion _last_setup_region; size_t _page_size; HeapWord* _bottom; - HeapWord* volatile _top; - HeapWord* _end; + Atomic _top; + Atomic _end; void numa_setup_pages(MemRegion mr, bool clear_space); @@ -64,21 +65,20 @@ class MutableSpace: public CHeapObj { protected: size_t page_size() const { return _page_size; } + Atomic* top_addr() { return &_top; } + public: virtual ~MutableSpace() = default; MutableSpace(size_t page_size); // Accessors HeapWord* bottom() const { return _bottom; } - HeapWord* top() const { return _top; } - HeapWord* end() const { return _end; } + HeapWord* top() const { return _top.load_relaxed(); } + HeapWord* end() const { return _end.load_relaxed(); } void set_bottom(HeapWord* value) { _bottom = value; } - virtual void set_top(HeapWord* value) { _top = value; } - void set_end(HeapWord* value) { _end = value; } - - HeapWord* volatile* top_addr() { return &_top; } - HeapWord** end_addr() { return &_end; } + virtual void set_top(HeapWord* value) { _top.store_relaxed(value); } + void set_end(HeapWord* value) { _end.store_relaxed(value); } MemRegion region() const { return MemRegion(bottom(), end()); } @@ -110,7 +110,7 @@ public: // Boolean queries. bool is_empty() const { return used_in_words() == 0; } bool not_empty() const { return used_in_words() > 0; } - bool contains(const void* p) const { return _bottom <= p && p < _end; } + bool contains(const void* p) const { return _bottom <= p && p < end(); } // Size computations. Sizes are in bytes. size_t used_in_bytes() const { return used_in_words() * HeapWordSize; } diff --git a/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp b/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp index f69219a1f40..f8dabc4539e 100644 --- a/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp +++ b/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp @@ -47,8 +47,8 @@ nonstatic_field(PSVirtualSpace, _committed_high_addr, char*) \ \ nonstatic_field(MutableSpace, _bottom, HeapWord*) \ - nonstatic_field(MutableSpace, _end, HeapWord*) \ - volatile_nonstatic_field(MutableSpace, _top, HeapWord*) \ + nonstatic_field(MutableSpace, _end, Atomic) \ + volatile_nonstatic_field(MutableSpace, _top, Atomic) \ \ nonstatic_field(PSYoungGen, _reserved, MemRegion) \ nonstatic_field(PSYoungGen, _virtual_space, PSVirtualSpace*) \ diff --git a/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp b/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp index ebc1c1c7fb7..914358760aa 100644 --- a/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp +++ b/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp @@ -104,11 +104,8 @@ void CardTableBarrierSetC1::post_barrier(LIRAccess& access, LIR_Opr addr, LIR_Op return; } - BarrierSet* bs = BarrierSet::barrier_set(); - CardTableBarrierSet* ctbs = barrier_set_cast(bs); - CardTable* ct = ctbs->card_table(); - LIR_Const* card_table_base = new LIR_Const(ct->byte_map_base()); - SHENANDOAHGC_ONLY(assert(!UseShenandoahGC, "Shenandoah byte_map_base is not constant.");) + CardTableBarrierSet* ctbs = barrier_set_cast(BarrierSet::barrier_set()); + LIR_Const* card_table_base = new LIR_Const(ctbs->card_table_base_const()); if (addr->is_address()) { LIR_Address* address = addr->as_address_ptr(); diff --git a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp index fada2672e9f..42af77ebdf4 100644 --- a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp @@ -116,7 +116,7 @@ Node* CardTableBarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& access Node* CardTableBarrierSetC2::byte_map_base_node(GraphKit* kit) const { // Get base of card map - CardTable::CardValue* card_table_base = ci_card_table_address(); + CardTable::CardValue* card_table_base = ci_card_table_address_const(); if (card_table_base != nullptr) { return kit->makecon(TypeRawPtr::make((address)card_table_base)); } else { diff --git a/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp b/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp index 539e40820a8..d6541198858 100644 --- a/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp +++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -73,15 +73,15 @@ CardTableBarrierSet::CardTableBarrierSet(CardTable* card_table) : {} CardTableBarrierSet::~CardTableBarrierSet() { - delete _card_table; + delete card_table(); } void CardTableBarrierSet::write_region(MemRegion mr) { - _card_table->dirty_MemRegion(mr); + card_table()->dirty_MemRegion(mr); } void CardTableBarrierSet::print_on(outputStream* st) const { - _card_table->print_on(st); + card_table()->print_on(st); } // Helper for ReduceInitialCardMarks. For performance, @@ -116,7 +116,7 @@ void CardTableBarrierSet::on_slowpath_allocation_exit(JavaThread* thread, oop ne if (!ReduceInitialCardMarks) { return; } - if (new_obj->is_typeArray() || _card_table->is_in_young(new_obj)) { + if (new_obj->is_typeArray() || card_table()->is_in_young(new_obj)) { // Arrays of non-references don't need a post-barrier. } else { MemRegion mr(cast_from_oop(new_obj), new_obj->size()); diff --git a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp index faeb007c77d..3a9b46d9df8 100644 --- a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp +++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -27,7 +27,9 @@ #include "gc/shared/barrierSet.hpp" #include "gc/shared/cardTable.hpp" +#include "gc/shared/gc_globals.hpp" #include "memory/memRegion.hpp" +#include "runtime/atomic.hpp" #include "utilities/align.hpp" // This kind of "BarrierSet" allows a "CollectedHeap" to detect and @@ -48,7 +50,7 @@ class CardTableBarrierSet: public BarrierSet { protected: typedef CardTable::CardValue CardValue; - CardTable* _card_table; + Atomic _card_table; CardTableBarrierSet(BarrierSetAssembler* barrier_set_assembler, BarrierSetC1* barrier_set_c1, @@ -60,6 +62,10 @@ public: CardTableBarrierSet(CardTable* card_table); virtual ~CardTableBarrierSet(); + inline static CardTableBarrierSet* barrier_set() { + return barrier_set_cast(BarrierSet::barrier_set()); + } + template inline void write_ref_field_pre(T* addr) {} @@ -85,7 +91,13 @@ public: // at the address "start", which may not necessarily be HeapWord-aligned inline void write_ref_array(HeapWord* start, size_t count); - CardTable* card_table() const { return _card_table; } + CardTable* card_table() { return _card_table.load_relaxed(); } + CardTable* card_table() const { return _card_table.load_relaxed(); } + + CardValue* card_table_base_const() const { + assert(UseSerialGC || UseParallelGC, "Only these GCs have constant card table base"); + return card_table()->byte_map_base(); + } virtual void on_slowpath_allocation_exit(JavaThread* thread, oop new_obj); diff --git a/src/hotspot/share/gc/shared/cardTableBarrierSet.inline.hpp b/src/hotspot/share/gc/shared/cardTableBarrierSet.inline.hpp index ea539a70be5..f60a7f47a19 100644 --- a/src/hotspot/share/gc/shared/cardTableBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ template inline void CardTableBarrierSet::write_ref_field_post(T* field) { - volatile CardValue* byte = _card_table->byte_for(field); + volatile CardValue* byte = card_table()->byte_for(field); *byte = CardTable::dirty_card_val(); } diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index d08e95378f7..6aa1fcf066b 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.hpp @@ -261,6 +261,7 @@ develop(uintx, ObjArrayMarkingStride, 2048, \ "Number of object array elements to push onto the marking stack " \ "before pushing a continuation entry") \ + range(1, INT_MAX/2) \ \ product_pd(bool, NeverActAsServerClassMachine, \ "(Deprecated) Never act like a server-class machine") \ diff --git a/src/hotspot/share/gc/shared/oopStorageSetParState.inline.hpp b/src/hotspot/share/gc/shared/oopStorageSetParState.inline.hpp index 8e220e745e5..8341a3b20c4 100644 --- a/src/hotspot/share/gc/shared/oopStorageSetParState.inline.hpp +++ b/src/hotspot/share/gc/shared/oopStorageSetParState.inline.hpp @@ -31,7 +31,6 @@ #include "gc/shared/oopStorageSet.hpp" #include "memory/iterator.hpp" #include "oops/access.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/debug.hpp" template diff --git a/src/hotspot/share/gc/shared/partialArrayState.cpp b/src/hotspot/share/gc/shared/partialArrayState.cpp index 6f714d48a35..aadbc46b7c1 100644 --- a/src/hotspot/share/gc/shared/partialArrayState.cpp +++ b/src/hotspot/share/gc/shared/partialArrayState.cpp @@ -28,7 +28,6 @@ #include "memory/arena.hpp" #include "nmt/memTag.hpp" #include "oops/oopsHierarchy.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/orderAccess.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp b/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp index aaa86e2de16..6946f7c69ff 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp @@ -28,7 +28,6 @@ #include "gc/shared/partialArrayTaskStepper.hpp" #include "gc/shared/partialArrayState.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/checkedCast.hpp" #include "utilities/debug.hpp" diff --git a/src/hotspot/share/gc/shared/preservedMarks.cpp b/src/hotspot/share/gc/shared/preservedMarks.cpp index 1c9f1c82e6f..605b7afe072 100644 --- a/src/hotspot/share/gc/shared/preservedMarks.cpp +++ b/src/hotspot/share/gc/shared/preservedMarks.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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,7 +29,7 @@ #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "utilities/macros.hpp" void PreservedMarks::restore() { @@ -55,15 +55,6 @@ void PreservedMarks::adjust_during_full_gc() { } } -void PreservedMarks::restore_and_increment(volatile size_t* const total_size_addr) { - const size_t stack_size = size(); - restore(); - // Only do the atomic add if the size is > 0. - if (stack_size > 0) { - AtomicAccess::add(total_size_addr, stack_size); - } -} - #ifndef PRODUCT void PreservedMarks::assert_empty() { assert(_stack.is_empty(), "stack expected to be empty, size = %zu", @@ -93,7 +84,7 @@ void PreservedMarksSet::init(uint num) { class RestorePreservedMarksTask : public WorkerTask { PreservedMarksSet* const _preserved_marks_set; SequentialSubTasksDone _sub_tasks; - volatile size_t _total_size; + Atomic _total_size; #ifdef ASSERT size_t _total_size_before; #endif // ASSERT @@ -102,7 +93,12 @@ public: void work(uint worker_id) override { uint task_id = 0; while (_sub_tasks.try_claim_task(task_id)) { - _preserved_marks_set->get(task_id)->restore_and_increment(&_total_size); + PreservedMarks* next = _preserved_marks_set->get(task_id); + size_t num_restored = next->size(); + next->restore(); + if (num_restored > 0) { + _total_size.add_then_fetch(num_restored); + } } } @@ -121,9 +117,11 @@ public: } ~RestorePreservedMarksTask() { - assert(_total_size == _total_size_before, "total_size = %zu before = %zu", _total_size, _total_size_before); - size_t mem_size = _total_size * (sizeof(oop) + sizeof(markWord)); - log_trace(gc)("Restored %zu marks, occupying %zu %s", _total_size, + size_t local_total_size = _total_size.load_relaxed(); + + assert(local_total_size == _total_size_before, "total_size = %zu before = %zu", local_total_size, _total_size_before); + size_t mem_size = local_total_size * (sizeof(oop) + sizeof(markWord)); + log_trace(gc)("Restored %zu marks, occupying %zu %s", local_total_size, byte_size_in_proper_unit(mem_size), proper_unit_for_byte_size(mem_size)); } diff --git a/src/hotspot/share/gc/shared/preservedMarks.hpp b/src/hotspot/share/gc/shared/preservedMarks.hpp index 10f75116524..3bbbd335011 100644 --- a/src/hotspot/share/gc/shared/preservedMarks.hpp +++ b/src/hotspot/share/gc/shared/preservedMarks.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -59,8 +59,7 @@ public: size_t size() const { return _stack.size(); } inline void push_if_necessary(oop obj, markWord m); inline void push_always(oop obj, markWord m); - // Iterate over the stack, restore all preserved marks, and - // reclaim the memory taken up by the stack segments. + // Restore all preserved marks, and reclaim the memory taken up by the stack segments. void restore(); // Adjust the preserved mark according to its @@ -71,8 +70,6 @@ public: // to their forwarding location stored in the mark. void adjust_during_full_gc(); - void restore_and_increment(volatile size_t* const _total_size_addr); - // Assert the stack is empty and has no cached segments. void assert_empty() PRODUCT_RETURN; diff --git a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp index df7d8f7b38d..0371ed2c73b 100644 --- a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp +++ b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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,7 +30,6 @@ #include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" -#include "runtime/atomicAccess.hpp" #define ASSERT_REF_TYPE(ref_type) assert((ref_type) >= REF_SOFT && (ref_type) <= REF_PHANTOM, \ "Invariant (%d)", (int)ref_type) @@ -196,7 +195,7 @@ void ReferenceProcessorPhaseTimes::reset() { _soft_weak_final_refs_phase_worker_time_sec->reset(); for (int i = 0; i < number_of_subclasses_of_ref; i++) { - _ref_dropped[i] = 0; + _ref_dropped[i].store_relaxed(0); _ref_discovered[i] = 0; } @@ -214,7 +213,7 @@ ReferenceProcessorPhaseTimes::~ReferenceProcessorPhaseTimes() { void ReferenceProcessorPhaseTimes::add_ref_dropped(ReferenceType ref_type, size_t count) { ASSERT_REF_TYPE(ref_type); - AtomicAccess::add(&_ref_dropped[ref_type_2_index(ref_type)], count, memory_order_relaxed); + _ref_dropped[ref_type_2_index(ref_type)].add_then_fetch(count, memory_order_relaxed); } void ReferenceProcessorPhaseTimes::set_ref_discovered(ReferenceType ref_type, size_t count) { @@ -271,7 +270,7 @@ void ReferenceProcessorPhaseTimes::print_reference(ReferenceType ref_type, uint int const ref_type_index = ref_type_2_index(ref_type); size_t discovered = _ref_discovered[ref_type_index]; - size_t dropped = _ref_dropped[ref_type_index]; + size_t dropped = _ref_dropped[ref_type_index].load_relaxed(); assert(discovered >= dropped, "invariant"); size_t processed = discovered - dropped; diff --git a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.hpp b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.hpp index 16691452ef4..82d26902bce 100644 --- a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.hpp +++ b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 "gc/shared/workerDataArray.hpp" #include "memory/allocation.hpp" #include "memory/referenceType.hpp" +#include "runtime/atomic.hpp" #include "utilities/ticks.hpp" class DiscoveredList; @@ -52,7 +53,7 @@ class ReferenceProcessorPhaseTimes : public CHeapObj { // Total spent time for reference processing. double _total_time_ms; - size_t _ref_dropped[number_of_subclasses_of_ref]; + Atomic _ref_dropped[number_of_subclasses_of_ref]; size_t _ref_discovered[number_of_subclasses_of_ref]; bool _processing_is_mt; diff --git a/src/hotspot/share/gc/shared/space.cpp b/src/hotspot/share/gc/shared/space.cpp index 011a0f5cfd8..84ba21527fd 100644 --- a/src/hotspot/share/gc/shared/space.cpp +++ b/src/hotspot/share/gc/shared/space.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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,7 +30,6 @@ #include "memory/iterator.inline.hpp" #include "memory/universe.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/java.hpp" #include "runtime/safepoint.hpp" #include "utilities/align.hpp" @@ -69,7 +68,7 @@ void ContiguousSpace::clear(bool mangle_space) { #ifndef PRODUCT void ContiguousSpace::mangle_unused_area() { - mangle_unused_area(MemRegion(_top, _end)); + mangle_unused_area(MemRegion(top(), _end)); } void ContiguousSpace::mangle_unused_area(MemRegion mr) { @@ -128,11 +127,8 @@ inline HeapWord* ContiguousSpace::par_allocate_impl(size_t size) { HeapWord* obj = top(); if (pointer_delta(end(), obj) >= size) { HeapWord* new_top = obj + size; - HeapWord* result = AtomicAccess::cmpxchg(top_addr(), obj, new_top); - // result can be one of two: - // the old top value: the exchange succeeded - // otherwise: the new value of the top is returned. - if (result == obj) { + // Retry if we did not successfully updated the top pointers. + if (_top.compare_set(obj, new_top)) { assert(is_object_aligned(obj) && is_object_aligned(new_top), "checking alignment"); return obj; } diff --git a/src/hotspot/share/gc/shared/space.hpp b/src/hotspot/share/gc/shared/space.hpp index 7f2887275b3..05b22f680bf 100644 --- a/src/hotspot/share/gc/shared/space.hpp +++ b/src/hotspot/share/gc/shared/space.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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,6 +32,7 @@ #include "memory/iterator.hpp" #include "memory/memRegion.hpp" #include "oops/markWord.hpp" +#include "runtime/atomic.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/align.hpp" #include "utilities/macros.hpp" @@ -53,7 +54,7 @@ class ContiguousSpace: public CHeapObj { private: HeapWord* _bottom; HeapWord* _end; - HeapWord* _top; + Atomic _top; // Allocation helpers (return null if full). inline HeapWord* allocate_impl(size_t word_size); @@ -64,12 +65,12 @@ public: // Accessors HeapWord* bottom() const { return _bottom; } - HeapWord* end() const { return _end; } - HeapWord* top() const { return _top; } + HeapWord* end() const { return _end; } + HeapWord* top() const { return _top.load_relaxed(); } void set_bottom(HeapWord* value) { _bottom = value; } void set_end(HeapWord* value) { _end = value; } - void set_top(HeapWord* value) { _top = value; } + void set_top(HeapWord* value) { _top.store_relaxed(value); } // Testers bool is_empty() const { return used() == 0; } @@ -121,9 +122,6 @@ public: // Iteration void object_iterate(ObjectClosure* blk); - // Addresses for inlined allocation - HeapWord** top_addr() { return &_top; } - // Debugging void verify() const; }; diff --git a/src/hotspot/share/gc/shared/taskqueue.cpp b/src/hotspot/share/gc/shared/taskqueue.cpp index f75dc4c2923..58af1793a48 100644 --- a/src/hotspot/share/gc/shared/taskqueue.cpp +++ b/src/hotspot/share/gc/shared/taskqueue.cpp @@ -25,7 +25,6 @@ #include "gc/shared/taskqueue.hpp" #include "logging/log.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/javaThread.hpp" #include "runtime/os.hpp" #include "utilities/debug.hpp" diff --git a/src/hotspot/share/gc/shared/taskqueue.inline.hpp b/src/hotspot/share/gc/shared/taskqueue.inline.hpp index 55851495a5f..e77645f4fcf 100644 --- a/src/hotspot/share/gc/shared/taskqueue.inline.hpp +++ b/src/hotspot/share/gc/shared/taskqueue.inline.hpp @@ -32,7 +32,6 @@ #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/orderAccess.hpp" #include "utilities/debug.hpp" #include "utilities/ostream.hpp" diff --git a/src/hotspot/share/gc/shared/vmStructs_gc.hpp b/src/hotspot/share/gc/shared/vmStructs_gc.hpp index db968e28f67..9348fd980f4 100644 --- a/src/hotspot/share/gc/shared/vmStructs_gc.hpp +++ b/src/hotspot/share/gc/shared/vmStructs_gc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -48,6 +48,7 @@ #if INCLUDE_ZGC #include "gc/z/vmStructs_z.hpp" #endif +#include "runtime/atomic.hpp" #define VM_STRUCTS_GC(nonstatic_field, \ volatile_static_field, \ @@ -88,7 +89,7 @@ nonstatic_field(CardTable, _byte_map_size, const size_t) \ nonstatic_field(CardTable, _byte_map, CardTable::CardValue*) \ nonstatic_field(CardTable, _byte_map_base, CardTable::CardValue*) \ - nonstatic_field(CardTableBarrierSet, _card_table, CardTable*) \ + nonstatic_field(CardTableBarrierSet, _card_table, Atomic) \ \ static_field(CollectedHeap, _lab_alignment_reserve, size_t) \ nonstatic_field(CollectedHeap, _reserved, MemRegion) \ @@ -97,7 +98,7 @@ \ nonstatic_field(ContiguousSpace, _bottom, HeapWord*) \ nonstatic_field(ContiguousSpace, _end, HeapWord*) \ - nonstatic_field(ContiguousSpace, _top, HeapWord*) \ + nonstatic_field(ContiguousSpace, _top, Atomic) \ \ nonstatic_field(MemRegion, _start, HeapWord*) \ nonstatic_field(MemRegion, _word_size, size_t) @@ -149,6 +150,7 @@ \ declare_toplevel_type(BarrierSet*) \ declare_toplevel_type(CardTable*) \ + declare_toplevel_type(Atomic) \ declare_toplevel_type(CardTable*const) \ declare_toplevel_type(CardTableBarrierSet*) \ declare_toplevel_type(CardTableBarrierSet**) \ diff --git a/src/hotspot/share/gc/shared/workerThread.cpp b/src/hotspot/share/gc/shared/workerThread.cpp index e4831d25d26..2f6f003608f 100644 --- a/src/hotspot/share/gc/shared/workerThread.cpp +++ b/src/hotspot/share/gc/shared/workerThread.cpp @@ -26,7 +26,6 @@ #include "gc/shared/workerThread.hpp" #include "logging/log.hpp" #include "memory/iterator.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/init.hpp" #include "runtime/java.hpp" #include "runtime/os.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp index 2aa37d7c575..004558a9fa8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp @@ -72,21 +72,33 @@ void ShenandoahBarrierSet::print_on(outputStream* st) const { bool ShenandoahBarrierSet::need_load_reference_barrier(DecoratorSet decorators, BasicType type) { if (!ShenandoahLoadRefBarrier) return false; - // Only needed for references return is_reference_type(type); } bool ShenandoahBarrierSet::need_keep_alive_barrier(DecoratorSet decorators, BasicType type) { if (!ShenandoahSATBBarrier) return false; - // Only needed for references if (!is_reference_type(type)) return false; - bool keep_alive = (decorators & AS_NO_KEEPALIVE) == 0; bool unknown = (decorators & ON_UNKNOWN_OOP_REF) != 0; bool on_weak_ref = (decorators & (ON_WEAK_OOP_REF | ON_PHANTOM_OOP_REF)) != 0; return (on_weak_ref || unknown) && keep_alive; } +bool ShenandoahBarrierSet::need_satb_barrier(DecoratorSet decorators, BasicType type) { + if (!ShenandoahSATBBarrier) return false; + if (!is_reference_type(type)) return false; + bool as_normal = (decorators & AS_NORMAL) != 0; + bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0; + return as_normal && !dest_uninitialized; +} + +bool ShenandoahBarrierSet::need_card_barrier(DecoratorSet decorators, BasicType type) { + if (!ShenandoahCardBarrier) return false; + if (!is_reference_type(type)) return false; + bool in_heap = (decorators & IN_HEAP) != 0; + return in_heap; +} + void ShenandoahBarrierSet::on_slowpath_allocation_exit(JavaThread* thread, oop new_obj) { #if COMPILER2_OR_JVMCI if (ReduceInitialCardMarks && ShenandoahCardBarrier && !ShenandoahHeap::heap()->is_in_young(new_obj)) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp index 28765605267..e7a0ed57740 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp @@ -60,6 +60,8 @@ public: static bool need_load_reference_barrier(DecoratorSet decorators, BasicType type); static bool need_keep_alive_barrier(DecoratorSet decorators, BasicType type); + static bool need_satb_barrier(DecoratorSet decorators, BasicType type); + static bool need_card_barrier(DecoratorSet decorators, BasicType type); static bool is_strong_access(DecoratorSet decorators) { return (decorators & (ON_WEAK_OOP_REF | ON_PHANTOM_OOP_REF)) == 0; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index 590e7831f07..bc11659c5e5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -44,7 +44,7 @@ ShenandoahControlThread::ShenandoahControlThread() : ShenandoahController(), _requested_gc_cause(GCCause::_no_gc), _degen_point(ShenandoahGC::_degenerated_outside_cycle), - _control_lock(Mutex::nosafepoint - 2, "ShenandoahGCRequest_lock", true) { + _control_lock(CONTROL_LOCK_RANK, "ShenandoahControl_lock", true) { set_name("Shenandoah Control Thread"); create_and_start(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahController.hpp b/src/hotspot/share/gc/shenandoah/shenandoahController.hpp index a6a699fac3b..b8ff4df4771 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahController.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahController.hpp @@ -42,6 +42,9 @@ private: shenandoah_padding(1); protected: + const Mutex::Rank WAITERS_LOCK_RANK = Mutex::safepoint - 5; + const Mutex::Rank CONTROL_LOCK_RANK = Mutex::nosafepoint - 2; + // While we could have a single lock for these, it may risk unblocking // GC waiters when alloc failure GC cycle finishes. We want instead // to make complete explicit cycle for demanding customers. @@ -54,8 +57,8 @@ protected: public: ShenandoahController(): _gc_id(0), - _alloc_failure_waiters_lock(Mutex::safepoint-2, "ShenandoahAllocFailureGC_lock", true), - _gc_waiters_lock(Mutex::safepoint-2, "ShenandoahRequestedGC_lock", true) + _alloc_failure_waiters_lock(WAITERS_LOCK_RANK, "ShenandoahAllocFailureWaiters_lock", true), + _gc_waiters_lock(WAITERS_LOCK_RANK, "ShenandoahGCWaiters_lock", true) { } // Request a collection cycle. This handles "explicit" gc requests diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index 018b4898a19..3b57190cc75 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -47,7 +47,7 @@ #include "utilities/events.hpp" ShenandoahGenerationalControlThread::ShenandoahGenerationalControlThread() : - _control_lock(Mutex::nosafepoint - 2, "ShenandoahGCRequest_lock", true), + _control_lock(CONTROL_LOCK_RANK, "ShenandoahGCRequest_lock", true), _requested_gc_cause(GCCause::_no_gc), _requested_generation(nullptr), _gc_mode(none), diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index ef99bd98c93..ccfc1c036c2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -2719,18 +2719,6 @@ bool ShenandoahRegionIterator::has_next() const { return _index < _heap->num_regions(); } -char ShenandoahHeap::gc_state() const { - return _gc_state.raw_value(); -} - -bool ShenandoahHeap::is_gc_state(GCState state) const { - // If the global gc state has been changed, but hasn't yet been propagated to all threads, then - // the global gc state is the correct value. Once the gc state has been synchronized with all threads, - // _gc_state_changed will be toggled to false and we need to use the thread local state. - return _gc_state_changed ? _gc_state.is_set(state) : ShenandoahThreadLocalData::is_gc_state(state); -} - - ShenandoahLiveData* ShenandoahHeap::get_liveness_cache(uint worker_id) { #ifdef ASSERT assert(_liveness_cache != nullptr, "sanity"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 174001170f4..9240091070b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -353,7 +353,7 @@ private: public: // This returns the raw value of the singular, global gc state. - char gc_state() const; + inline char gc_state() const; // Compares the given state against either the global gc state, or the thread local state. // The global gc state may change on a safepoint and is the correct value to use until @@ -361,7 +361,7 @@ public: // compare against the thread local state). The thread local gc state may also be changed // by a handshake operation, in which case, this function continues using the updated thread // local value. - bool is_gc_state(GCState state) const; + inline bool is_gc_state(GCState state) const; // This copies the global gc state into a thread local variable for all threads. // The thread local gc state is primarily intended to support quick access at barriers. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index 34c279a1495..e35f116b843 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -452,6 +452,17 @@ inline bool ShenandoahHeap::in_collection_set_loc(void* p) const { return collection_set()->is_in_loc(p); } +inline char ShenandoahHeap::gc_state() const { + return _gc_state.raw_value(); +} + +inline bool ShenandoahHeap::is_gc_state(GCState state) const { + // If the global gc state has been changed, but hasn't yet been propagated to all threads, then + // the global gc state is the correct value. Once the gc state has been synchronized with all threads, + // _gc_state_changed will be toggled to false and we need to use the thread local state. + return _gc_state_changed ? _gc_state.is_set(state) : ShenandoahThreadLocalData::is_gc_state(state); +} + inline bool ShenandoahHeap::is_idle() const { return _gc_state_changed ? _gc_state.is_clear() : ShenandoahThreadLocalData::gc_state(Thread::current()) == 0; } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp index 849459157b5..ba24e890769 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp @@ -226,8 +226,6 @@ inline void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, assert(obj->is_objArray(), "expect object array"); objArrayOop array = objArrayOop(obj); - assert (ObjArrayMarkingStride > 0, "sanity"); - // Split out tasks, as suggested in ShenandoahMarkTask docs. Avoid pushing tasks that // are known to start beyond the array. while ((1 << pow) > (int)ObjArrayMarkingStride && (chunk*2 < ShenandoahMarkTask::chunk_size())) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index aed768b9db1..d5e34d02b13 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -63,7 +63,7 @@ private: uint _nworkers; ShenandoahHeapRegion** _coalesce_and_fill_region_array; uint _coalesce_and_fill_region_count; - volatile bool _is_preempted; + Atomic _is_preempted; public: ShenandoahConcurrentCoalesceAndFillTask(uint nworkers, @@ -88,7 +88,7 @@ public: if (!r->oop_coalesce_and_fill(true)) { // Coalesce and fill has been preempted - AtomicAccess::store(&_is_preempted, true); + _is_preempted.store_relaxed(true); return; } } @@ -96,7 +96,7 @@ public: // Value returned from is_completed() is only valid after all worker thread have terminated. bool is_completed() { - return !AtomicAccess::load(&_is_preempted); + return !_is_preempted.load_relaxed(); } }; @@ -147,23 +147,23 @@ void ShenandoahOldGeneration::augment_promoted_reserve(size_t increment) { void ShenandoahOldGeneration::reset_promoted_expended() { shenandoah_assert_heaplocked_or_safepoint(); - AtomicAccess::store(&_promoted_expended, static_cast(0)); - AtomicAccess::store(&_promotion_failure_count, static_cast(0)); - AtomicAccess::store(&_promotion_failure_words, static_cast(0)); + _promoted_expended.store_relaxed(0); + _promotion_failure_count.store_relaxed(0); + _promotion_failure_words.store_relaxed(0); } size_t ShenandoahOldGeneration::expend_promoted(size_t increment) { shenandoah_assert_heaplocked_or_safepoint(); assert(get_promoted_expended() + increment <= get_promoted_reserve(), "Do not expend more promotion than budgeted"); - return AtomicAccess::add(&_promoted_expended, increment); + return _promoted_expended.add_then_fetch(increment); } size_t ShenandoahOldGeneration::unexpend_promoted(size_t decrement) { - return AtomicAccess::sub(&_promoted_expended, decrement); + return _promoted_expended.sub_then_fetch(decrement); } size_t ShenandoahOldGeneration::get_promoted_expended() const { - return AtomicAccess::load(&_promoted_expended); + return _promoted_expended.load_relaxed(); } bool ShenandoahOldGeneration::can_allocate(const ShenandoahAllocRequest &req) const { @@ -582,8 +582,8 @@ void ShenandoahOldGeneration::handle_failed_evacuation() { } void ShenandoahOldGeneration::handle_failed_promotion(Thread* thread, size_t size) { - AtomicAccess::inc(&_promotion_failure_count); - AtomicAccess::add(&_promotion_failure_words, size); + _promotion_failure_count.add_then_fetch(1UL); + _promotion_failure_words.and_then_fetch(size); LogTarget(Debug, gc, plab) lt; LogStream ls(lt); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index 633d2c9f617..5ebad461f3c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -64,7 +64,7 @@ private: // is therefore always accessed through atomic operations. This is increased when a // PLAB is allocated for promotions. The value is decreased by the amount of memory // remaining in a PLAB when it is retired. - size_t _promoted_expended; + Atomic _promoted_expended; // Represents the quantity of live bytes we expect to promote during the next GC cycle, either by // evacuation or by promote-in-place. This value is used by the young heuristic to trigger mixed collections. @@ -78,8 +78,8 @@ private: // Keep track of the number and size of promotions that failed. Perhaps we should use this to increase // the size of the old generation for the next collection cycle. - size_t _promotion_failure_count; - size_t _promotion_failure_words; + Atomic _promotion_failure_count; + Atomic _promotion_failure_words; // During construction of the collection set, we keep track of regions that are eligible // for promotion in place. These fields track the count of those humongous and regular regions. @@ -126,8 +126,8 @@ public: size_t get_promoted_expended() const; // Return the count and size (in words) of failed promotions since the last reset - size_t get_promotion_failed_count() const { return AtomicAccess::load(&_promotion_failure_count); } - size_t get_promotion_failed_words() const { return AtomicAccess::load(&_promotion_failure_words); } + size_t get_promotion_failed_count() const { return _promotion_failure_count.load_relaxed(); } + size_t get_promotion_failed_words() const { return _promotion_failure_words.load_relaxed(); } // Test if there is enough memory reserved for this promotion bool can_promote(size_t requested_bytes) const { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index 0cc6d4c6ed4..b60f8128d1d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -110,15 +110,15 @@ private: void do_oop_work(T* p) { T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { - oop obj = CompressedOops::decode_not_null(o); + // Basic verification should happen before we touch anything else. + // For performance reasons, only fully verify non-marked field values. + // We are here when the host object for *p is already marked. + oop obj = CompressedOops::decode_raw_not_null(o); + verify_oop_at_basic(p, obj); + if (is_instance_ref_klass(ShenandoahForwarding::klass(obj))) { obj = ShenandoahForwarding::get_forwardee(obj); } - // Single threaded verification can use faster non-atomic stack and bitmap - // methods. - // - // For performance reasons, only fully verify non-marked field values. - // We are here when the host object for *p is already marked. if (in_generation(obj) && _map->par_mark(obj)) { verify_oop_at(p, obj); _stack->push(ShenandoahVerifierTask(obj)); @@ -131,7 +131,7 @@ private: return _generation->contains(region); } - void verify_oop(oop obj) { + void verify_oop(oop obj, bool basic = false) { // Perform consistency checks with gradually decreasing safety level. This guarantees // that failure report would not try to touch something that was not yet verified to be // safe to process. @@ -174,10 +174,14 @@ private: } } + check(ShenandoahAsserts::_safe_unknown, obj, obj_reg->is_active(), + "Object should be in active region"); + // ------------ obj is safe at this point -------------- - check(ShenandoahAsserts::_safe_oop, obj, obj_reg->is_active(), - "Object should be in active region"); + if (basic) { + return; + } switch (_options._verify_liveness) { case ShenandoahVerifier::_verify_liveness_disable: @@ -331,6 +335,18 @@ public: _interior_loc = nullptr; } + /** + * Verify object with known interior reference, with only basic verification. + * @param p interior reference where the object is referenced from; can be off-heap + * @param obj verified object + */ + template + void verify_oop_at_basic(T* p, oop obj) { + _interior_loc = p; + verify_oop(obj, /* basic = */ true); + _interior_loc = nullptr; + } + /** * Verify object without known interior reference. * Useful when picking up the object at known offset in heap, @@ -1232,7 +1248,9 @@ private: void do_oop_work(T* p) { T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { - oop obj = CompressedOops::decode_not_null(o); + oop obj = CompressedOops::decode_raw_not_null(o); + ShenandoahAsserts::assert_correct(p, obj, __FILE__, __LINE__); + oop fwd = ShenandoahForwarding::get_forwardee_raw_unchecked(obj); if (obj != fwd) { ShenandoahAsserts::print_failure(ShenandoahAsserts::_safe_all, obj, p, nullptr, @@ -1252,7 +1270,9 @@ private: void do_oop_work(T* p) { T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { - oop obj = CompressedOops::decode_not_null(o); + oop obj = CompressedOops::decode_raw_not_null(o); + ShenandoahAsserts::assert_correct(p, obj, __FILE__, __LINE__); + ShenandoahHeap* heap = ShenandoahHeap::heap(); if (!heap->marking_context()->is_marked_or_old(obj)) { @@ -1306,7 +1326,9 @@ public: inline void work(T* p) { T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { - oop obj = CompressedOops::decode_not_null(o); + oop obj = CompressedOops::decode_raw_not_null(o); + ShenandoahAsserts::assert_correct(p, obj, __FILE__, __LINE__); + if (_heap->is_in_young(obj) && !_scanner->is_card_dirty((HeapWord*) p)) { ShenandoahAsserts::print_failure(ShenandoahAsserts::_safe_all, obj, p, nullptr, _message, "clean card, it should be dirty.", __FILE__, __LINE__); diff --git a/src/hotspot/share/gc/z/vmStructs_z.hpp b/src/hotspot/share/gc/z/vmStructs_z.hpp index a88dae188d3..de32b964a51 100644 --- a/src/hotspot/share/gc/z/vmStructs_z.hpp +++ b/src/hotspot/share/gc/z/vmStructs_z.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -111,7 +111,6 @@ typedef ZValue ZPerNUMAZPartition; \ nonstatic_field(ZForwarding, _virtual, const ZVirtualMemory) \ nonstatic_field(ZForwarding, _object_alignment_shift, const size_t) \ - volatile_nonstatic_field(ZForwarding, _ref_count, int) \ nonstatic_field(ZForwarding, _entries, const ZAttachedArrayForForwarding) \ nonstatic_field(ZForwardingEntry, _entry, uint64_t) \ nonstatic_field(ZAttachedArrayForForwarding, _length, const size_t) diff --git a/src/hotspot/share/gc/z/zAbort.cpp b/src/hotspot/share/gc/z/zAbort.cpp index 3310793f730..82523ddebe8 100644 --- a/src/hotspot/share/gc/z/zAbort.cpp +++ b/src/hotspot/share/gc/z/zAbort.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -22,10 +22,9 @@ */ #include "gc/z/zAbort.hpp" -#include "runtime/atomicAccess.hpp" -volatile bool ZAbort::_should_abort = false; +Atomic ZAbort::_should_abort{}; void ZAbort::abort() { - AtomicAccess::store(&_should_abort, true); + _should_abort.store_relaxed(true); } diff --git a/src/hotspot/share/gc/z/zAbort.hpp b/src/hotspot/share/gc/z/zAbort.hpp index 925b0a79ac3..64633752d5d 100644 --- a/src/hotspot/share/gc/z/zAbort.hpp +++ b/src/hotspot/share/gc/z/zAbort.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -25,10 +25,11 @@ #define SHARE_GC_Z_ZABORT_HPP #include "memory/allStatic.hpp" +#include "runtime/atomic.hpp" class ZAbort : public AllStatic { private: - static volatile bool _should_abort; + static Atomic _should_abort; public: static bool should_abort(); diff --git a/src/hotspot/share/gc/z/zAbort.inline.hpp b/src/hotspot/share/gc/z/zAbort.inline.hpp index 37503e25f70..856179e9d2a 100644 --- a/src/hotspot/share/gc/z/zAbort.inline.hpp +++ b/src/hotspot/share/gc/z/zAbort.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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,10 +26,8 @@ #include "gc/z/zAbort.hpp" -#include "runtime/atomicAccess.hpp" - inline bool ZAbort::should_abort() { - return AtomicAccess::load(&_should_abort); + return _should_abort.load_relaxed(); } #endif // SHARE_GC_Z_ZABORT_INLINE_HPP diff --git a/src/hotspot/share/gc/z/zAddress.inline.hpp b/src/hotspot/share/gc/z/zAddress.inline.hpp index c8c8ec7ae3a..0b99802729b 100644 --- a/src/hotspot/share/gc/z/zAddress.inline.hpp +++ b/src/hotspot/share/gc/z/zAddress.inline.hpp @@ -199,18 +199,18 @@ CREATE_ZOFFSET_OPERATORS(zoffset) inline uintptr_t untype(zbacking_offset offset) { const uintptr_t value = static_cast(offset); - assert(value < ZBackingOffsetMax, "Offset out of bounds (" PTR_FORMAT " < " PTR_FORMAT ")", value, ZAddressOffsetMax); + assert(value < ZBackingOffsetMax, "Offset out of bounds (" PTR_FORMAT " < " PTR_FORMAT ")", value, ZBackingOffsetMax); return value; } inline uintptr_t untype(zbacking_offset_end offset) { const uintptr_t value = static_cast(offset); - assert(value <= ZBackingOffsetMax, "Offset out of bounds (" PTR_FORMAT " <= " PTR_FORMAT ")", value, ZAddressOffsetMax); + assert(value <= ZBackingOffsetMax, "Offset out of bounds (" PTR_FORMAT " <= " PTR_FORMAT ")", value, ZBackingOffsetMax); return value; } inline zbacking_offset to_zbacking_offset(uintptr_t value) { - assert(value < ZBackingOffsetMax, "Value out of bounds (" PTR_FORMAT " < " PTR_FORMAT ")", value, ZAddressOffsetMax); + assert(value < ZBackingOffsetMax, "Value out of bounds (" PTR_FORMAT " < " PTR_FORMAT ")", value, ZBackingOffsetMax); return zbacking_offset(value); } @@ -227,7 +227,7 @@ inline zbacking_offset_end to_zbacking_offset_end(zbacking_offset start, size_t } inline zbacking_offset_end to_zbacking_offset_end(uintptr_t value) { - assert(value <= ZBackingOffsetMax, "Value out of bounds (" PTR_FORMAT " <= " PTR_FORMAT ")", value, ZAddressOffsetMax); + assert(value <= ZBackingOffsetMax, "Value out of bounds (" PTR_FORMAT " <= " PTR_FORMAT ")", value, ZBackingOffsetMax); return zbacking_offset_end(value); } diff --git a/src/hotspot/share/gc/z/zArray.hpp b/src/hotspot/share/gc/z/zArray.hpp index 2c2f8a5dbfb..d39def4096e 100644 --- a/src/hotspot/share/gc/z/zArray.hpp +++ b/src/hotspot/share/gc/z/zArray.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ #include "cppstdlib/type_traits.hpp" #include "memory/allocation.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/os.hpp" #include "runtime/thread.hpp" #include "utilities/growableArray.hpp" @@ -78,7 +78,9 @@ public: template class ZArrayIteratorImpl : public StackObj { private: - size_t _next; + using NextType = std::conditional_t, size_t>; + + NextType _next; const size_t _end; const T* const _array; diff --git a/src/hotspot/share/gc/z/zArray.inline.hpp b/src/hotspot/share/gc/z/zArray.inline.hpp index 9e2bc19118e..15caa54ebab 100644 --- a/src/hotspot/share/gc/z/zArray.inline.hpp +++ b/src/hotspot/share/gc/z/zArray.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -27,7 +27,6 @@ #include "gc/z/zArray.hpp" #include "gc/z/zLock.inline.hpp" -#include "runtime/atomicAccess.hpp" template ZArraySlice::ZArraySlice(T* data, int len) @@ -130,7 +129,7 @@ inline bool ZArrayIteratorImpl::next_serial(size_t* index) { template inline bool ZArrayIteratorImpl::next_parallel(size_t* index) { - const size_t claimed_index = AtomicAccess::fetch_then_add(&_next, 1u, memory_order_relaxed); + const size_t claimed_index = _next.fetch_then_add(1u, memory_order_relaxed); if (claimed_index < _end) { *index = claimed_index; @@ -177,7 +176,7 @@ inline bool ZArrayIteratorImpl::next_if(T* elem, Function predicate template inline bool ZArrayIteratorImpl::next_index(size_t* index) { - if (Parallel) { + if constexpr (Parallel) { return next_parallel(index); } else { return next_serial(index); diff --git a/src/hotspot/share/gc/z/zForwarding.cpp b/src/hotspot/share/gc/z/zForwarding.cpp index 820bb9dbc35..92fc1bc89df 100644 --- a/src/hotspot/share/gc/z/zForwarding.cpp +++ b/src/hotspot/share/gc/z/zForwarding.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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,7 +29,6 @@ #include "gc/z/zStat.hpp" #include "gc/z/zUtils.inline.hpp" #include "logging/log.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/align.hpp" // @@ -50,7 +49,7 @@ // bool ZForwarding::claim() { - return AtomicAccess::cmpxchg(&_claimed, false, true) == false; + return _claimed.compare_set(false, true); } void ZForwarding::in_place_relocation_start(zoffset relocated_watermark) { @@ -60,7 +59,7 @@ void ZForwarding::in_place_relocation_start(zoffset relocated_watermark) { // Support for ZHeap::is_in checks of from-space objects // in a page that is in-place relocating - AtomicAccess::store(&_in_place_thread, Thread::current()); + _in_place_thread.store_relaxed(Thread::current()); _in_place_top_at_start = _page->top(); } @@ -76,17 +75,17 @@ void ZForwarding::in_place_relocation_finish() { } // Disable relaxed ZHeap::is_in checks - AtomicAccess::store(&_in_place_thread, (Thread*)nullptr); + _in_place_thread.store_relaxed(nullptr); } bool ZForwarding::in_place_relocation_is_below_top_at_start(zoffset offset) const { // Only the relocating thread is allowed to know about the old relocation top. - return AtomicAccess::load(&_in_place_thread) == Thread::current() && offset < _in_place_top_at_start; + return _in_place_thread.load_relaxed() == Thread::current() && offset < _in_place_top_at_start; } bool ZForwarding::retain_page(ZRelocateQueue* queue) { for (;;) { - const int32_t ref_count = AtomicAccess::load_acquire(&_ref_count); + const int32_t ref_count = _ref_count.load_acquire(); if (ref_count == 0) { // Released @@ -101,7 +100,7 @@ bool ZForwarding::retain_page(ZRelocateQueue* queue) { return false; } - if (AtomicAccess::cmpxchg(&_ref_count, ref_count, ref_count + 1) == ref_count) { + if (_ref_count.compare_set(ref_count, ref_count + 1)) { // Retained return true; } @@ -110,11 +109,11 @@ bool ZForwarding::retain_page(ZRelocateQueue* queue) { void ZForwarding::in_place_relocation_claim_page() { for (;;) { - const int32_t ref_count = AtomicAccess::load(&_ref_count); + const int32_t ref_count = _ref_count.load_relaxed(); assert(ref_count > 0, "Invalid state"); // Invert reference count - if (AtomicAccess::cmpxchg(&_ref_count, ref_count, -ref_count) != ref_count) { + if (!_ref_count.compare_set(ref_count, -ref_count)) { continue; } @@ -122,7 +121,7 @@ void ZForwarding::in_place_relocation_claim_page() { // and we have now claimed the page. Otherwise we wait until it is claimed. if (ref_count != 1) { ZLocker locker(&_ref_lock); - while (AtomicAccess::load_acquire(&_ref_count) != -1) { + while (_ref_count.load_acquire() != -1) { _ref_lock.wait(); } } @@ -134,12 +133,12 @@ void ZForwarding::in_place_relocation_claim_page() { void ZForwarding::release_page() { for (;;) { - const int32_t ref_count = AtomicAccess::load(&_ref_count); + const int32_t ref_count = _ref_count.load_relaxed(); assert(ref_count != 0, "Invalid state"); if (ref_count > 0) { // Decrement reference count - if (AtomicAccess::cmpxchg(&_ref_count, ref_count, ref_count - 1) != ref_count) { + if (!_ref_count.compare_set(ref_count, ref_count - 1)) { continue; } @@ -152,7 +151,7 @@ void ZForwarding::release_page() { } } else { // Increment reference count - if (AtomicAccess::cmpxchg(&_ref_count, ref_count, ref_count + 1) != ref_count) { + if (!_ref_count.compare_set(ref_count, ref_count + 1)) { continue; } @@ -171,9 +170,9 @@ void ZForwarding::release_page() { ZPage* ZForwarding::detach_page() { // Wait until released - if (AtomicAccess::load_acquire(&_ref_count) != 0) { + if (_ref_count.load_acquire() != 0) { ZLocker locker(&_ref_lock); - while (AtomicAccess::load_acquire(&_ref_count) != 0) { + while (_ref_count.load_acquire() != 0) { _ref_lock.wait(); } } @@ -182,16 +181,16 @@ ZPage* ZForwarding::detach_page() { } ZPage* ZForwarding::page() { - assert(AtomicAccess::load(&_ref_count) != 0, "The page has been released/detached"); + assert(_ref_count.load_relaxed() != 0, "The page has been released/detached"); return _page; } void ZForwarding::mark_done() { - AtomicAccess::store(&_done, true); + _done.store_relaxed(true); } bool ZForwarding::is_done() const { - return AtomicAccess::load(&_done); + return _done.load_relaxed(); } // @@ -288,7 +287,7 @@ void ZForwarding::relocated_remembered_fields_publish() { // used to have remembered set entries. Now publish the fields to // the YC. - const ZPublishState res = AtomicAccess::cmpxchg(&_relocated_remembered_fields_state, ZPublishState::none, ZPublishState::published); + const ZPublishState res = _relocated_remembered_fields_state.compare_exchange(ZPublishState::none, ZPublishState::published); // none: OK to publish // published: Not possible - this operation makes this transition @@ -319,7 +318,7 @@ void ZForwarding::relocated_remembered_fields_notify_concurrent_scan_of() { // Invariant: The page is being retained assert(ZGeneration::young()->is_phase_mark(), "Only called when"); - const ZPublishState res = AtomicAccess::cmpxchg(&_relocated_remembered_fields_state, ZPublishState::none, ZPublishState::reject); + const ZPublishState res = _relocated_remembered_fields_state.compare_exchange(ZPublishState::none, ZPublishState::reject); // none: OC has not completed relocation // published: OC has completed and published all relocated remembered fields @@ -340,7 +339,7 @@ void ZForwarding::relocated_remembered_fields_notify_concurrent_scan_of() { // OC relocation already collected and published fields // Still notify concurrent scanning and reject the collected data from the OC - const ZPublishState res2 = AtomicAccess::cmpxchg(&_relocated_remembered_fields_state, ZPublishState::published, ZPublishState::reject); + const ZPublishState res2 = _relocated_remembered_fields_state.compare_exchange(ZPublishState::published, ZPublishState::reject); assert(res2 == ZPublishState::published, "Should not fail"); log_debug(gc, remset)("Forwarding remset eager and reject: " PTR_FORMAT " " PTR_FORMAT, untype(start()), untype(end())); @@ -368,7 +367,7 @@ bool ZForwarding::relocated_remembered_fields_published_contains(volatile zpoint } void ZForwarding::verify() const { - guarantee(_ref_count != 0, "Invalid reference count"); + guarantee(_ref_count.load_relaxed() != 0, "Invalid reference count"); guarantee(_page != nullptr, "Invalid page"); uint32_t live_objects = 0; diff --git a/src/hotspot/share/gc/z/zForwarding.hpp b/src/hotspot/share/gc/z/zForwarding.hpp index 29b5cf4aabe..72319bd2ebb 100644 --- a/src/hotspot/share/gc/z/zForwarding.hpp +++ b/src/hotspot/share/gc/z/zForwarding.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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,6 +32,7 @@ #include "gc/z/zPageAge.hpp" #include "gc/z/zPageType.hpp" #include "gc/z/zVirtualMemory.hpp" +#include "runtime/atomic.hpp" class ObjectClosure; class ZForwardingAllocator; @@ -62,13 +63,13 @@ private: const uint32_t _partition_id; const ZPageAge _from_age; const ZPageAge _to_age; - volatile bool _claimed; + Atomic _claimed; mutable ZConditionLock _ref_lock; - volatile int32_t _ref_count; - volatile bool _done; + Atomic _ref_count; + Atomic _done; // Relocated remembered set fields support - volatile ZPublishState _relocated_remembered_fields_state; + Atomic _relocated_remembered_fields_state; PointerArray _relocated_remembered_fields_array; uint32_t _relocated_remembered_fields_publish_young_seqnum; @@ -77,7 +78,7 @@ private: zoffset_end _in_place_top_at_start; // Debugging - volatile Thread* _in_place_thread; + Atomic _in_place_thread; ZForwardingEntry* entries() const; ZForwardingEntry at(ZForwardingCursor* cursor) const; diff --git a/src/hotspot/share/gc/z/zForwarding.inline.hpp b/src/hotspot/share/gc/z/zForwarding.inline.hpp index 45b5d495e79..02f61eb5b05 100644 --- a/src/hotspot/share/gc/z/zForwarding.inline.hpp +++ b/src/hotspot/share/gc/z/zForwarding.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -196,7 +196,7 @@ void ZForwarding::oops_do_in_forwarded_via_table(Function function) { } inline bool ZForwarding::in_place_relocation() const { - assert(AtomicAccess::load(&_ref_count) != 0, "The page has been released/detached"); + assert(_ref_count.load_relaxed() != 0, "The page has been released/detached"); return _in_place; } @@ -307,7 +307,7 @@ inline void ZForwarding::relocated_remembered_fields_register(volatile zpointer* // Invariant: Page is being retained assert(ZGeneration::young()->is_phase_mark(), "Only called when"); - const ZPublishState res = AtomicAccess::load(&_relocated_remembered_fields_state); + const ZPublishState res = _relocated_remembered_fields_state.load_relaxed(); // none: Gather remembered fields // published: Have already published fields - not possible since they haven't been @@ -327,7 +327,7 @@ inline void ZForwarding::relocated_remembered_fields_register(volatile zpointer* // Returns true iff the page is being (or about to be) relocated by the OC // while the YC gathered the remembered fields of the "from" page. inline bool ZForwarding::relocated_remembered_fields_is_concurrently_scanned() const { - return AtomicAccess::load(&_relocated_remembered_fields_state) == ZPublishState::reject; + return _relocated_remembered_fields_state.load_relaxed() == ZPublishState::reject; } template @@ -335,7 +335,7 @@ inline void ZForwarding::relocated_remembered_fields_apply_to_published(Function // Invariant: Page is not being retained assert(ZGeneration::young()->is_phase_mark(), "Only called when"); - const ZPublishState res = AtomicAccess::load_acquire(&_relocated_remembered_fields_state); + const ZPublishState res = _relocated_remembered_fields_state.load_acquire(); // none: Nothing published - page had already been relocated before YC started // published: OC relocated and published relocated remembered fields @@ -363,14 +363,14 @@ inline void ZForwarding::relocated_remembered_fields_apply_to_published(Function // collection. Mark that it is unsafe (and unnecessary) to call scan_page // on the page in the page table. assert(res != ZPublishState::accept, "Unexpected"); - AtomicAccess::store(&_relocated_remembered_fields_state, ZPublishState::reject); + _relocated_remembered_fields_state.store_relaxed(ZPublishState::reject); } else { log_debug(gc, remset)("scan_forwarding failed retain safe " PTR_FORMAT, untype(start())); // Guaranteed that the page was fully relocated and removed from page table. // Because of this we can signal to scan_page that any page found in page table // of the same slot as the current forwarding is a page that is safe to scan, // and in fact must be scanned. - AtomicAccess::store(&_relocated_remembered_fields_state, ZPublishState::accept); + _relocated_remembered_fields_state.store_relaxed(ZPublishState::accept); } } diff --git a/src/hotspot/share/gc/z/zForwardingAllocator.cpp b/src/hotspot/share/gc/z/zForwardingAllocator.cpp index 0b4e6c7b08c..451a1d62754 100644 --- a/src/hotspot/share/gc/z/zForwardingAllocator.cpp +++ b/src/hotspot/share/gc/z/zForwardingAllocator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -34,6 +34,7 @@ ZForwardingAllocator::~ZForwardingAllocator() { } void ZForwardingAllocator::reset(size_t size) { - _start = _top = REALLOC_C_HEAP_ARRAY(char, _start, size, mtGC); + _start = REALLOC_C_HEAP_ARRAY(char, _start, size, mtGC); + _top.store_relaxed(_start); _end = _start + size; } diff --git a/src/hotspot/share/gc/z/zForwardingAllocator.hpp b/src/hotspot/share/gc/z/zForwardingAllocator.hpp index 8424fe6cd70..62d84885692 100644 --- a/src/hotspot/share/gc/z/zForwardingAllocator.hpp +++ b/src/hotspot/share/gc/z/zForwardingAllocator.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -24,13 +24,14 @@ #ifndef SHARE_GC_Z_ZFORWARDINGALLOCATOR_HPP #define SHARE_GC_Z_ZFORWARDINGALLOCATOR_HPP +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" class ZForwardingAllocator { private: - char* _start; - char* _end; - char* _top; + char* _start; + char* _end; + Atomic _top; public: ZForwardingAllocator(); diff --git a/src/hotspot/share/gc/z/zForwardingAllocator.inline.hpp b/src/hotspot/share/gc/z/zForwardingAllocator.inline.hpp index dc71507175c..c8811c84ede 100644 --- a/src/hotspot/share/gc/z/zForwardingAllocator.inline.hpp +++ b/src/hotspot/share/gc/z/zForwardingAllocator.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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,6 @@ #include "gc/z/zForwardingAllocator.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/debug.hpp" inline size_t ZForwardingAllocator::size() const { @@ -34,11 +33,11 @@ inline size_t ZForwardingAllocator::size() const { } inline bool ZForwardingAllocator::is_full() const { - return _top == _end; + return _top.load_relaxed() == _end; } inline void* ZForwardingAllocator::alloc(size_t size) { - char* const addr = AtomicAccess::fetch_then_add(&_top, size); + char* const addr = _top.fetch_then_add(size); assert(addr + size <= _end, "Allocation should never fail"); return addr; } diff --git a/src/hotspot/share/gc/z/zGeneration.cpp b/src/hotspot/share/gc/z/zGeneration.cpp index a8c646bf895..27f352a624f 100644 --- a/src/hotspot/share/gc/z/zGeneration.cpp +++ b/src/hotspot/share/gc/z/zGeneration.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -56,7 +56,6 @@ #include "logging/log.hpp" #include "memory/universe.hpp" #include "prims/jvmtiTagMap.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/continuation.hpp" #include "runtime/handshake.hpp" #include "runtime/safepoint.hpp" @@ -298,33 +297,33 @@ bool ZGeneration::is_relocate_queue_active() const { void ZGeneration::reset_statistics() { assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint"); - _freed = 0; - _promoted = 0; - _compacted = 0; + _freed.store_relaxed(0u); + _promoted.store_relaxed(0u); + _compacted.store_relaxed(0u); } size_t ZGeneration::freed() const { - return _freed; + return _freed.load_relaxed(); } void ZGeneration::increase_freed(size_t size) { - AtomicAccess::add(&_freed, size, memory_order_relaxed); + _freed.add_then_fetch(size, memory_order_relaxed); } size_t ZGeneration::promoted() const { - return _promoted; + return _promoted.load_relaxed();; } void ZGeneration::increase_promoted(size_t size) { - AtomicAccess::add(&_promoted, size, memory_order_relaxed); + _promoted.add_then_fetch(size, memory_order_relaxed); } size_t ZGeneration::compacted() const { - return _compacted; + return _compacted.load_relaxed();; } void ZGeneration::increase_compacted(size_t size) { - AtomicAccess::add(&_compacted, size, memory_order_relaxed); + _compacted.add_then_fetch(size, memory_order_relaxed); } ConcurrentGCTimer* ZGeneration::gc_timer() const { diff --git a/src/hotspot/share/gc/z/zGeneration.hpp b/src/hotspot/share/gc/z/zGeneration.hpp index 3b700361460..1371865958b 100644 --- a/src/hotspot/share/gc/z/zGeneration.hpp +++ b/src/hotspot/share/gc/z/zGeneration.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -37,6 +37,7 @@ #include "gc/z/zWeakRootsProcessor.hpp" #include "gc/z/zWorkers.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" class ThreadClosure; class ZForwardingTable; @@ -70,9 +71,9 @@ protected: ZRelocate _relocate; ZRelocationSet _relocation_set; - volatile size_t _freed; - volatile size_t _promoted; - volatile size_t _compacted; + Atomic _freed; + Atomic _promoted; + Atomic _compacted; Phase _phase; uint32_t _seqnum; diff --git a/src/hotspot/share/gc/z/zJNICritical.cpp b/src/hotspot/share/gc/z/zJNICritical.cpp index 28cc70cb3b5..f50cc113936 100644 --- a/src/hotspot/share/gc/z/zJNICritical.cpp +++ b/src/hotspot/share/gc/z/zJNICritical.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -24,7 +24,6 @@ #include "gc/z/zJNICritical.hpp" #include "gc/z/zLock.inline.hpp" #include "gc/z/zStat.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/thread.inline.hpp" #include "utilities/debug.hpp" @@ -46,22 +45,22 @@ static const ZStatCriticalPhase ZCriticalPhaseJNICriticalStall("JNI Critical Stall", false /* verbose */); -volatile int64_t ZJNICritical::_count; -ZConditionLock* ZJNICritical::_lock; +Atomic ZJNICritical::_count; +ZConditionLock* ZJNICritical::_lock; void ZJNICritical::initialize() { - _count = 0; + precond(_count.load_relaxed() == 0); _lock = new ZConditionLock(); } void ZJNICritical::block() { for (;;) { - const int64_t count = AtomicAccess::load_acquire(&_count); + const int64_t count = _count.load_acquire(); if (count < 0) { // Already blocked, wait until unblocked ZLocker locker(_lock); - while (AtomicAccess::load_acquire(&_count) < 0) { + while (_count.load_acquire() < 0) { _lock->wait(); } @@ -70,7 +69,7 @@ void ZJNICritical::block() { } // Increment and invert count - if (AtomicAccess::cmpxchg(&_count, count, -(count + 1)) != count) { + if (!_count.compare_set(count, -(count + 1))) { continue; } @@ -80,7 +79,7 @@ void ZJNICritical::block() { if (count != 0) { // Wait until blocked ZLocker locker(_lock); - while (AtomicAccess::load_acquire(&_count) != -1) { + while (_count.load_acquire() != -1) { _lock->wait(); } } @@ -91,18 +90,18 @@ void ZJNICritical::block() { } void ZJNICritical::unblock() { - const int64_t count = AtomicAccess::load_acquire(&_count); + const int64_t count = _count.load_acquire(); assert(count == -1, "Invalid count"); // Notify unblocked ZLocker locker(_lock); - AtomicAccess::release_store(&_count, (int64_t)0); + _count.release_store(0); _lock->notify_all(); } void ZJNICritical::enter_inner(JavaThread* thread) { for (;;) { - const int64_t count = AtomicAccess::load_acquire(&_count); + const int64_t count = _count.load_acquire(); if (count < 0) { // Wait until unblocked @@ -112,7 +111,7 @@ void ZJNICritical::enter_inner(JavaThread* thread) { ThreadBlockInVM tbivm(thread); ZLocker locker(_lock); - while (AtomicAccess::load_acquire(&_count) < 0) { + while (_count.load_acquire() < 0) { _lock->wait(); } @@ -121,7 +120,7 @@ void ZJNICritical::enter_inner(JavaThread* thread) { } // Increment count - if (AtomicAccess::cmpxchg(&_count, count, count + 1) != count) { + if (!_count.compare_set(count, count + 1)) { continue; } @@ -142,17 +141,17 @@ void ZJNICritical::enter(JavaThread* thread) { void ZJNICritical::exit_inner() { for (;;) { - const int64_t count = AtomicAccess::load_acquire(&_count); + const int64_t count = _count.load_acquire(); assert(count != 0, "Invalid count"); if (count > 0) { // No block in progress, decrement count - if (AtomicAccess::cmpxchg(&_count, count, count - 1) != count) { + if (!_count.compare_set(count, count - 1)) { continue; } } else { // Block in progress, increment count - if (AtomicAccess::cmpxchg(&_count, count, count + 1) != count) { + if (!_count.compare_set(count, count + 1)) { continue; } diff --git a/src/hotspot/share/gc/z/zJNICritical.hpp b/src/hotspot/share/gc/z/zJNICritical.hpp index d2ba80eddba..def6001ae9f 100644 --- a/src/hotspot/share/gc/z/zJNICritical.hpp +++ b/src/hotspot/share/gc/z/zJNICritical.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -25,14 +25,15 @@ #define SHARE_GC_Z_ZJNICRITICAL_HPP #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" class JavaThread; class ZConditionLock; class ZJNICritical : public AllStatic { private: - static volatile int64_t _count; - static ZConditionLock* _lock; + static Atomic _count; + static ZConditionLock* _lock; static void enter_inner(JavaThread* thread); static void exit_inner(); diff --git a/src/hotspot/share/gc/z/zLiveMap.cpp b/src/hotspot/share/gc/z/zLiveMap.cpp index c4ae41b0873..8b03a97e03f 100644 --- a/src/hotspot/share/gc/z/zLiveMap.cpp +++ b/src/hotspot/share/gc/z/zLiveMap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -27,7 +27,6 @@ #include "gc/z/zStat.hpp" #include "gc/z/zUtils.hpp" #include "logging/log.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/debug.hpp" #include "utilities/powerOfTwo.hpp" #include "utilities/spinYield.hpp" @@ -60,18 +59,18 @@ void ZLiveMap::reset(ZGenerationId id) { // Multiple threads can enter here, make sure only one of them // resets the marking information while the others busy wait. - for (uint32_t seqnum = AtomicAccess::load_acquire(&_seqnum); + for (uint32_t seqnum = _seqnum.load_acquire(); seqnum != generation->seqnum(); - seqnum = AtomicAccess::load_acquire(&_seqnum)) { + seqnum = _seqnum.load_acquire()) { if (seqnum != seqnum_initializing) { // No one has claimed initialization of the livemap yet - if (AtomicAccess::cmpxchg(&_seqnum, seqnum, seqnum_initializing) == seqnum) { + if (_seqnum.compare_set(seqnum, seqnum_initializing)) { // This thread claimed the initialization // Reset marking information - _live_bytes = 0; - _live_objects = 0; + _live_bytes.store_relaxed(0u); + _live_objects.store_relaxed(0u); // Clear segment claimed/live bits segment_live_bits().clear(); @@ -81,13 +80,13 @@ void ZLiveMap::reset(ZGenerationId id) { // a bit is about to be set for the first time. initialize_bitmap(); - assert(_seqnum == seqnum_initializing, "Invalid"); + assert(_seqnum.load_relaxed() == seqnum_initializing, "Invalid"); // Make sure the newly reset marking information is ordered // before the update of the page seqnum, such that when the // up-to-date seqnum is load acquired, the bit maps will not // contain stale information. - AtomicAccess::release_store(&_seqnum, generation->seqnum()); + _seqnum.release_store(generation->seqnum()); break; } } diff --git a/src/hotspot/share/gc/z/zLiveMap.hpp b/src/hotspot/share/gc/z/zLiveMap.hpp index 71457d05a41..9373f6aad98 100644 --- a/src/hotspot/share/gc/z/zLiveMap.hpp +++ b/src/hotspot/share/gc/z/zLiveMap.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -28,6 +28,7 @@ #include "gc/z/zBitMap.hpp" #include "gc/z/zGenerationId.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" class ObjectClosure; @@ -41,9 +42,9 @@ private: const uint32_t _segment_size; const int _segment_shift; - volatile uint32_t _seqnum; - volatile uint32_t _live_objects; - volatile size_t _live_bytes; + Atomic _seqnum; + Atomic _live_objects; + Atomic _live_bytes; BitMap::bm_word_t _segment_live_bits; BitMap::bm_word_t _segment_claim_bits; ZBitMap _bitmap; diff --git a/src/hotspot/share/gc/z/zLiveMap.inline.hpp b/src/hotspot/share/gc/z/zLiveMap.inline.hpp index e6176b928ff..69687d0b595 100644 --- a/src/hotspot/share/gc/z/zLiveMap.inline.hpp +++ b/src/hotspot/share/gc/z/zLiveMap.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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,24 +31,23 @@ #include "gc/z/zGeneration.inline.hpp" #include "gc/z/zMark.hpp" #include "gc/z/zUtils.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/bitMap.inline.hpp" #include "utilities/debug.hpp" inline void ZLiveMap::reset() { - _seqnum = 0; + _seqnum.store_relaxed(0u); } inline bool ZLiveMap::is_marked(ZGenerationId id) const { - return AtomicAccess::load_acquire(&_seqnum) == ZGeneration::generation(id)->seqnum(); + return _seqnum.load_acquire() == ZGeneration::generation(id)->seqnum(); } inline uint32_t ZLiveMap::live_objects() const { - return _live_objects; + return _live_objects.load_relaxed(); } inline size_t ZLiveMap::live_bytes() const { - return _live_bytes; + return _live_bytes.load_relaxed(); } inline const BitMapView ZLiveMap::segment_live_bits() const { @@ -116,8 +115,8 @@ inline bool ZLiveMap::set(ZGenerationId id, BitMap::idx_t index, bool finalizabl } inline void ZLiveMap::inc_live(uint32_t objects, size_t bytes) { - AtomicAccess::add(&_live_objects, objects); - AtomicAccess::add(&_live_bytes, bytes); + _live_objects.add_then_fetch(objects); + _live_bytes.add_then_fetch(bytes); } inline BitMap::idx_t ZLiveMap::segment_start(BitMap::idx_t segment) const { diff --git a/src/hotspot/share/gc/z/zLock.hpp b/src/hotspot/share/gc/z/zLock.hpp index 640a9fb02d3..a843f2389d7 100644 --- a/src/hotspot/share/gc/z/zLock.hpp +++ b/src/hotspot/share/gc/z/zLock.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -25,6 +25,7 @@ #define SHARE_GC_Z_ZLOCK_HPP #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "runtime/mutex.hpp" class ZLock : public CHeapObj { @@ -39,9 +40,9 @@ public: class ZReentrantLock { private: - ZLock _lock; - Thread* volatile _owner; - uint64_t _count; + ZLock _lock; + Atomic _owner; + uint64_t _count; public: ZReentrantLock(); diff --git a/src/hotspot/share/gc/z/zLock.inline.hpp b/src/hotspot/share/gc/z/zLock.inline.hpp index edf59be5b4c..ba5d6692521 100644 --- a/src/hotspot/share/gc/z/zLock.inline.hpp +++ b/src/hotspot/share/gc/z/zLock.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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,6 @@ #include "gc/z/zLock.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/javaThread.hpp" #include "runtime/os.inline.hpp" #include "utilities/debug.hpp" @@ -50,11 +49,11 @@ inline ZReentrantLock::ZReentrantLock() inline void ZReentrantLock::lock() { Thread* const thread = Thread::current(); - Thread* const owner = AtomicAccess::load(&_owner); + Thread* const owner = _owner.load_relaxed(); if (owner != thread) { _lock.lock(); - AtomicAccess::store(&_owner, thread); + _owner.store_relaxed(thread); } _count++; @@ -67,14 +66,14 @@ inline void ZReentrantLock::unlock() { _count--; if (_count == 0) { - AtomicAccess::store(&_owner, (Thread*)nullptr); + _owner.store_relaxed(nullptr); _lock.unlock(); } } inline bool ZReentrantLock::is_owned() const { Thread* const thread = Thread::current(); - Thread* const owner = AtomicAccess::load(&_owner); + Thread* const owner = _owner.load_relaxed(); return owner == thread; } diff --git a/src/hotspot/share/gc/z/zMark.cpp b/src/hotspot/share/gc/z/zMark.cpp index 3b247fdd35e..03701ae9998 100644 --- a/src/hotspot/share/gc/z/zMark.cpp +++ b/src/hotspot/share/gc/z/zMark.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -57,7 +57,6 @@ #include "memory/iterator.inline.hpp" #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/continuation.hpp" #include "runtime/handshake.hpp" #include "runtime/javaThread.hpp" @@ -152,13 +151,14 @@ void ZMark::prepare_work() { _terminate.reset(_nworkers); // Reset flush counters - _work_nproactiveflush = _work_nterminateflush = 0; + _work_nproactiveflush.store_relaxed(0u); + _work_nterminateflush.store_relaxed(0u); } void ZMark::finish_work() { // Accumulate proactive/terminate flush counters - _nproactiveflush += _work_nproactiveflush; - _nterminateflush += _work_nterminateflush; + _nproactiveflush += _work_nproactiveflush.load_relaxed(); + _nterminateflush += _work_nterminateflush.load_relaxed(); } void ZMark::follow_work_complete() { @@ -594,7 +594,7 @@ bool ZMark::flush() { } bool ZMark::try_terminate_flush() { - AtomicAccess::inc(&_work_nterminateflush); + _work_nterminateflush.add_then_fetch(1u); _terminate.set_resurrected(false); if (ZVerifyMarking) { @@ -610,12 +610,12 @@ bool ZMark::try_proactive_flush() { return false; } - if (AtomicAccess::load(&_work_nproactiveflush) == ZMarkProactiveFlushMax) { + if (_work_nproactiveflush.load_relaxed() == ZMarkProactiveFlushMax) { // Limit reached or we're trying to terminate return false; } - AtomicAccess::inc(&_work_nproactiveflush); + _work_nproactiveflush.add_then_fetch(1u); SuspendibleThreadSetLeaver sts_leaver; return flush(); diff --git a/src/hotspot/share/gc/z/zMark.hpp b/src/hotspot/share/gc/z/zMark.hpp index cc398addeb8..72a527b957d 100644 --- a/src/hotspot/share/gc/z/zMark.hpp +++ b/src/hotspot/share/gc/z/zMark.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 "gc/z/zMarkStackEntry.hpp" #include "gc/z/zMarkTerminate.hpp" #include "oops/oopsHierarchy.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" class Thread; @@ -60,8 +61,8 @@ private: ZMarkingSMR _marking_smr; ZMarkStripeSet _stripes; ZMarkTerminate _terminate; - volatile size_t _work_nproactiveflush; - volatile size_t _work_nterminateflush; + Atomic _work_nproactiveflush; + Atomic _work_nterminateflush; size_t _nproactiveflush; size_t _nterminateflush; size_t _ntrycomplete; diff --git a/src/hotspot/share/gc/z/zMarkTerminate.hpp b/src/hotspot/share/gc/z/zMarkTerminate.hpp index cff1f8e73fa..9def3e72120 100644 --- a/src/hotspot/share/gc/z/zMarkTerminate.hpp +++ b/src/hotspot/share/gc/z/zMarkTerminate.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -25,6 +25,7 @@ #define SHARE_GC_Z_ZMARKTERMINATE_HPP #include "gc/z/zLock.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" class ZMarkStripeSet; @@ -32,9 +33,9 @@ class ZMarkStripeSet; class ZMarkTerminate { private: uint _nworkers; - volatile uint _nworking; - volatile uint _nawakening; - volatile bool _resurrected; + Atomic _nworking; + Atomic _nawakening; + Atomic _resurrected; ZConditionLock _lock; void maybe_reduce_stripes(ZMarkStripeSet* stripes, size_t used_nstripes); diff --git a/src/hotspot/share/gc/z/zMarkTerminate.inline.hpp b/src/hotspot/share/gc/z/zMarkTerminate.inline.hpp index 575044e3a39..84a545623bc 100644 --- a/src/hotspot/share/gc/z/zMarkTerminate.inline.hpp +++ b/src/hotspot/share/gc/z/zMarkTerminate.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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,7 +30,6 @@ #include "gc/z/zLock.inline.hpp" #include "gc/z/zMarkStack.hpp" #include "logging/log.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/osThread.hpp" #include "runtime/thread.inline.hpp" @@ -42,24 +41,23 @@ inline ZMarkTerminate::ZMarkTerminate() _lock() {} inline void ZMarkTerminate::reset(uint nworkers) { - AtomicAccess::store(&_nworkers, nworkers); - AtomicAccess::store(&_nworking, nworkers); - _nawakening = 0; + _nworkers = nworkers; + _nworking.store_relaxed(nworkers); + _nawakening.store_relaxed(0u); } inline void ZMarkTerminate::leave() { SuspendibleThreadSetLeaver sts_leaver; ZLocker locker(&_lock); - AtomicAccess::store(&_nworking, _nworking - 1); - if (_nworking == 0) { + if (_nworking.sub_then_fetch(1u, memory_order_relaxed) == 0) { // Last thread leaving; notify waiters _lock.notify_all(); } } inline void ZMarkTerminate::maybe_reduce_stripes(ZMarkStripeSet* stripes, size_t used_nstripes) { - size_t nstripes = stripes->nstripes(); + const size_t nstripes = stripes->nstripes(); if (used_nstripes == nstripes && nstripes > 1u) { stripes->try_set_nstripes(nstripes, nstripes >> 1); } @@ -69,8 +67,7 @@ inline bool ZMarkTerminate::try_terminate(ZMarkStripeSet* stripes, size_t used_n SuspendibleThreadSetLeaver sts_leaver; ZLocker locker(&_lock); - AtomicAccess::store(&_nworking, _nworking - 1); - if (_nworking == 0) { + if (_nworking.sub_then_fetch(1u, memory_order_relaxed) == 0) { // Last thread entering termination: success _lock.notify_all(); return true; @@ -83,24 +80,24 @@ inline bool ZMarkTerminate::try_terminate(ZMarkStripeSet* stripes, size_t used_n // We either got notification about more work // or got a spurious wakeup; don't terminate - if (_nawakening > 0) { - AtomicAccess::store(&_nawakening, _nawakening - 1); + if (_nawakening.load_relaxed() > 0) { + _nawakening.sub_then_fetch(1u, memory_order_relaxed); } - if (_nworking == 0) { + if (_nworking.load_relaxed() == 0) { // We got notified all work is done; terminate return true; } - AtomicAccess::store(&_nworking, _nworking + 1); + _nworking.add_then_fetch(1u, memory_order_relaxed); return false; } inline void ZMarkTerminate::wake_up() { - uint nworking = AtomicAccess::load(&_nworking); - uint nawakening = AtomicAccess::load(&_nawakening); - if (nworking + nawakening == AtomicAccess::load(&_nworkers)) { + const uint nworking = _nworking.load_relaxed(); + const uint nawakening = _nawakening.load_relaxed(); + if (nworking + nawakening == _nworkers) { // Everyone is working or about to return; } @@ -111,24 +108,24 @@ inline void ZMarkTerminate::wake_up() { } ZLocker locker(&_lock); - if (_nworking + _nawakening != _nworkers) { + if (_nworking.load_relaxed() + _nawakening.load_relaxed() != _nworkers) { // Everyone is not working - AtomicAccess::store(&_nawakening, _nawakening + 1); + _nawakening.add_then_fetch(1u, memory_order_relaxed); _lock.notify(); } } inline bool ZMarkTerminate::saturated() const { - uint nworking = AtomicAccess::load(&_nworking); - uint nawakening = AtomicAccess::load(&_nawakening); + const uint nworking = _nworking.load_relaxed(); + const uint nawakening = _nawakening.load_relaxed(); - return nworking + nawakening == AtomicAccess::load(&_nworkers); + return nworking + nawakening == _nworkers; } inline void ZMarkTerminate::set_resurrected(bool value) { // Update resurrected if it changed if (resurrected() != value) { - AtomicAccess::store(&_resurrected, value); + _resurrected.store_relaxed(value); if (value) { log_debug(gc, marking)("Resurrection broke termination"); } else { @@ -138,7 +135,7 @@ inline void ZMarkTerminate::set_resurrected(bool value) { } inline bool ZMarkTerminate::resurrected() const { - return AtomicAccess::load(&_resurrected); + return _resurrected.load_relaxed(); } #endif // SHARE_GC_Z_ZMARKTERMINATE_INLINE_HPP diff --git a/src/hotspot/share/gc/z/zNMethodTableIteration.cpp b/src/hotspot/share/gc/z/zNMethodTableIteration.cpp index bdd4270ddac..423e98dd28b 100644 --- a/src/hotspot/share/gc/z/zNMethodTableIteration.cpp +++ b/src/hotspot/share/gc/z/zNMethodTableIteration.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -24,7 +24,6 @@ #include "gc/z/zNMethodTableEntry.hpp" #include "gc/z/zNMethodTableIteration.hpp" #include "memory/iterator.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -42,11 +41,11 @@ void ZNMethodTableIteration::nmethods_do_begin(ZNMethodTableEntry* table, size_t _table = table; _size = size; - _claimed = 0; + _claimed.store_relaxed(0u); } void ZNMethodTableIteration::nmethods_do_end() { - assert(_claimed >= _size, "Failed to claim all table entries"); + assert(_claimed.load_relaxed() >= _size, "Failed to claim all table entries"); // Finish iteration _table = nullptr; @@ -57,7 +56,7 @@ void ZNMethodTableIteration::nmethods_do(NMethodClosure* cl) { // Claim table partition. Each partition is currently sized to span // two cache lines. This number is just a guess, but seems to work well. const size_t partition_size = (ZCacheLineSize * 2) / sizeof(ZNMethodTableEntry); - const size_t partition_start = MIN2(AtomicAccess::fetch_then_add(&_claimed, partition_size), _size); + const size_t partition_start = MIN2(_claimed.fetch_then_add(partition_size), _size); const size_t partition_end = MIN2(partition_start + partition_size, _size); if (partition_start == partition_end) { // End of table diff --git a/src/hotspot/share/gc/z/zNMethodTableIteration.hpp b/src/hotspot/share/gc/z/zNMethodTableIteration.hpp index 064c3eafca0..12dc096491d 100644 --- a/src/hotspot/share/gc/z/zNMethodTableIteration.hpp +++ b/src/hotspot/share/gc/z/zNMethodTableIteration.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -25,15 +25,16 @@ #define SHARE_GC_Z_ZNMETHODTABLEITERATION_HPP #include "gc/z/zGlobals.hpp" +#include "runtime/atomic.hpp" class NMethodClosure; class ZNMethodTableEntry; class ZNMethodTableIteration { private: - ZNMethodTableEntry* _table; - size_t _size; - ZCACHE_ALIGNED volatile size_t _claimed; + ZNMethodTableEntry* _table; + size_t _size; + ZCACHE_ALIGNED Atomic _claimed; bool in_progress() const; diff --git a/src/hotspot/share/gc/z/zRelocate.cpp b/src/hotspot/share/gc/z/zRelocate.cpp index da07f67d859..d51cf5abbae 100644 --- a/src/hotspot/share/gc/z/zRelocate.cpp +++ b/src/hotspot/share/gc/z/zRelocate.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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,31 +64,31 @@ ZRelocateQueue::ZRelocateQueue() _needs_attention(0) {} bool ZRelocateQueue::needs_attention() const { - return AtomicAccess::load(&_needs_attention) != 0; + return _needs_attention.load_relaxed() != 0; } void ZRelocateQueue::inc_needs_attention() { - const int needs_attention = AtomicAccess::add(&_needs_attention, 1); + const int needs_attention = _needs_attention.add_then_fetch(1); assert(needs_attention == 1 || needs_attention == 2, "Invalid state"); } void ZRelocateQueue::dec_needs_attention() { - const int needs_attention = AtomicAccess::sub(&_needs_attention, 1); + const int needs_attention = _needs_attention.sub_then_fetch(1); assert(needs_attention == 0 || needs_attention == 1, "Invalid state"); } void ZRelocateQueue::activate(uint nworkers) { - _is_active = true; + _is_active.store_relaxed(true); join(nworkers); } void ZRelocateQueue::deactivate() { - AtomicAccess::store(&_is_active, false); + _is_active.store_relaxed(false); clear(); } bool ZRelocateQueue::is_active() const { - return AtomicAccess::load(&_is_active); + return _is_active.load_relaxed(); } void ZRelocateQueue::join(uint nworkers) { @@ -453,7 +453,7 @@ static void retire_target_page(ZGeneration* generation, ZPage* page) { class ZRelocateSmallAllocator { private: ZGeneration* const _generation; - volatile size_t _in_place_count; + Atomic _in_place_count; public: ZRelocateSmallAllocator(ZGeneration* generation) @@ -463,7 +463,7 @@ public: ZPage* alloc_and_retire_target_page(ZForwarding* forwarding, ZPage* target) { ZPage* const page = alloc_page(forwarding); if (page == nullptr) { - AtomicAccess::inc(&_in_place_count); + _in_place_count.add_then_fetch(1u); } if (target != nullptr) { @@ -493,7 +493,7 @@ public: } size_t in_place_count() const { - return _in_place_count; + return _in_place_count.load_relaxed(); } }; @@ -503,7 +503,7 @@ private: ZConditionLock _lock; ZRelocationTargets* _shared_targets; bool _in_place; - volatile size_t _in_place_count; + Atomic _in_place_count; public: ZRelocateMediumAllocator(ZGeneration* generation, ZRelocationTargets* shared_targets) @@ -539,7 +539,7 @@ public: ZPage* const to_page = alloc_page(forwarding); _shared_targets->set(partition_id, to_age, to_page); if (to_page == nullptr) { - AtomicAccess::inc(&_in_place_count); + _in_place_count.add_then_fetch(1u); _in_place = true; } @@ -579,7 +579,7 @@ public: } size_t in_place_count() const { - return _in_place_count; + return _in_place_count.load_relaxed(); } }; diff --git a/src/hotspot/share/gc/z/zRelocate.hpp b/src/hotspot/share/gc/z/zRelocate.hpp index 038efba83eb..d25536e1bce 100644 --- a/src/hotspot/share/gc/z/zRelocate.hpp +++ b/src/hotspot/share/gc/z/zRelocate.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -28,6 +28,7 @@ #include "gc/z/zPageAge.hpp" #include "gc/z/zRelocationSet.hpp" #include "gc/z/zValue.hpp" +#include "runtime/atomic.hpp" class ZForwarding; class ZGeneration; @@ -42,8 +43,8 @@ private: uint _nworkers; uint _nsynchronized; bool _synchronize; - volatile bool _is_active; - volatile int _needs_attention; + Atomic _is_active; + Atomic _needs_attention; bool needs_attention() const; void inc_needs_attention(); diff --git a/src/hotspot/share/gc/z/zTLABUsage.cpp b/src/hotspot/share/gc/z/zTLABUsage.cpp index c2e3db6bedf..d3613cf8632 100644 --- a/src/hotspot/share/gc/z/zTLABUsage.cpp +++ b/src/hotspot/share/gc/z/zTLABUsage.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -23,24 +23,23 @@ #include "gc/z/zTLABUsage.hpp" #include "logging/log.hpp" -#include "runtime/atomicAccess.hpp" ZTLABUsage::ZTLABUsage() : _used(0), _used_history() {} void ZTLABUsage::increase_used(size_t size) { - AtomicAccess::add(&_used, size, memory_order_relaxed); + _used.add_then_fetch(size, memory_order_relaxed); } void ZTLABUsage::decrease_used(size_t size) { - precond(size <= _used); + precond(size <= _used.load_relaxed()); - AtomicAccess::sub(&_used, size, memory_order_relaxed); + _used.sub_then_fetch(size, memory_order_relaxed); } void ZTLABUsage::reset() { - const size_t used = AtomicAccess::xchg(&_used, (size_t) 0); + const size_t used = _used.exchange(0u); // Avoid updates when nothing has been allocated since the last YC if (used == 0) { diff --git a/src/hotspot/share/gc/z/zTLABUsage.hpp b/src/hotspot/share/gc/z/zTLABUsage.hpp index 3d1b084fe16..68c7620c26e 100644 --- a/src/hotspot/share/gc/z/zTLABUsage.hpp +++ b/src/hotspot/share/gc/z/zTLABUsage.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -24,6 +24,7 @@ #ifndef SHARE_GC_Z_ZTLABUSAGE_HPP #define SHARE_GC_Z_ZTLABUSAGE_HPP +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/numberSeq.hpp" @@ -42,9 +43,9 @@ class ZTLABUsage { private: // Accounting TLAB used until the next GC cycle - volatile size_t _used; + Atomic _used; // Sequence of historic used values - TruncatedSeq _used_history; + TruncatedSeq _used_history; public: ZTLABUsage(); diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp index 801a9bf8eb4..3e9d8c97b88 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp @@ -236,16 +236,23 @@ void CompilerToVM::Data::initialize(JVMCI_TRAPS) { JVMTI_ONLY( _should_notify_object_alloc = &JvmtiExport::_should_notify_object_alloc; ) BarrierSet* bs = BarrierSet::barrier_set(); +#if INCLUDE_G1GC + if (bs->is_a(BarrierSet::G1BarrierSet)) { + cardtable_start_address = nullptr; + cardtable_shift = CardTable::card_shift(); + } else +#endif +#if INCLUDE_SHENANDOAHGC + if (bs->is_a(BarrierSet::ShenandoahBarrierSet)) { + cardtable_start_address = nullptr; + cardtable_shift = CardTable::card_shift(); + } else +#endif if (bs->is_a(BarrierSet::CardTableBarrierSet)) { - CardTable::CardValue* base = ci_card_table_address(); + CardTable::CardValue* base = ci_card_table_address_const(); assert(base != nullptr, "unexpected byte_map_base"); cardtable_start_address = base; cardtable_shift = CardTable::card_shift(); -#if INCLUDE_SHENANDOAHGC - } else if (bs->is_a(BarrierSet::ShenandoahBarrierSet)) { - cardtable_start_address = nullptr; - cardtable_shift = CardTable::card_shift(); -#endif } else { // No card mark barriers cardtable_start_address = nullptr; diff --git a/src/hotspot/share/memory/allocation.cpp b/src/hotspot/share/memory/allocation.cpp index 9df91225d6f..2a109688ca6 100644 --- a/src/hotspot/share/memory/allocation.cpp +++ b/src/hotspot/share/memory/allocation.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -22,6 +22,7 @@ * */ +#include "cds/aotMetaspace.hpp" #include "memory/allocation.inline.hpp" #include "memory/arena.hpp" #include "memory/metaspace.hpp" @@ -161,12 +162,33 @@ void AnyObj::set_allocation_type(address res, allocation_type type) { } } +void AnyObj::set_in_aot_cache() { + _allocation_t[0] = 0; + _allocation_t[1] = 0; +} + +bool AnyObj::in_aot_cache() const { + if (AOTMetaspace::in_aot_cache(this)) { + precond(_allocation_t[0] == 0); + precond(_allocation_t[1] == 0); + return true; + } else { + return false; + } +} + AnyObj::allocation_type AnyObj::get_allocation_type() const { + if (in_aot_cache()) { + return STACK_OR_EMBEDDED; + } assert(~(_allocation_t[0] | allocation_mask) == (uintptr_t)this, "lost resource object"); return (allocation_type)((~_allocation_t[0]) & allocation_mask); } bool AnyObj::is_type_set() const { + if (in_aot_cache()) { + return true; + } allocation_type type = (allocation_type)(_allocation_t[1] & allocation_mask); return get_allocation_type() == type && (_allocation_t[1] - type) == (uintptr_t)(&_allocation_t[1]); diff --git a/src/hotspot/share/memory/allocation.hpp b/src/hotspot/share/memory/allocation.hpp index 963ca04aadf..15b7750a363 100644 --- a/src/hotspot/share/memory/allocation.hpp +++ b/src/hotspot/share/memory/allocation.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -420,6 +420,7 @@ class AnyObj { public: enum allocation_type { STACK_OR_EMBEDDED = 0, RESOURCE_AREA, C_HEAP, ARENA, allocation_mask = 0x3 }; static void set_allocation_type(address res, allocation_type type) NOT_DEBUG_RETURN; + void set_in_aot_cache() NOT_DEBUG_RETURN; #ifdef ASSERT private: // When this object is allocated on stack the new() operator is not @@ -429,6 +430,7 @@ class AnyObj { uintptr_t _allocation_t[2]; bool is_type_set() const; void initialize_allocation_info(); + bool in_aot_cache() const; public: allocation_type get_allocation_type() const; bool allocated_on_stack_or_embedded() const { return get_allocation_type() == STACK_OR_EMBEDDED; } diff --git a/src/hotspot/share/memory/arena.hpp b/src/hotspot/share/memory/arena.hpp index a8450b5543a..7d88c79ca52 100644 --- a/src/hotspot/share/memory/arena.hpp +++ b/src/hotspot/share/memory/arena.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -101,6 +101,7 @@ public: FN(ra, Resource areas) \ FN(node, C2 Node arena) \ FN(comp, C2 Compile arena) \ + FN(idealloop, C2 Ideal Loop arena) \ FN(type, C2 Type arena) \ FN(states, C2 Matcher States Arena) \ FN(reglive, C2 Register Allocation Live Arenas) \ diff --git a/src/hotspot/share/memory/metaspace/blockTree.cpp b/src/hotspot/share/memory/metaspace/blockTree.cpp index 7ad24353c96..bdae317a0b9 100644 --- a/src/hotspot/share/memory/metaspace/blockTree.cpp +++ b/src/hotspot/share/memory/metaspace/blockTree.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -39,18 +39,14 @@ const size_t BlockTree::MinWordSize; #define NODE_FORMAT \ "@" PTR_FORMAT \ ": canary " INTPTR_FORMAT \ - ", parent " PTR_FORMAT \ - ", left " PTR_FORMAT \ - ", right " PTR_FORMAT \ + ", tree " PTR_FORMAT \ ", next " PTR_FORMAT \ ", size %zu" #define NODE_FORMAT_ARGS(n) \ p2i(n), \ (n)->_canary, \ - p2i((n)->_parent), \ - p2i((n)->_left), \ - p2i((n)->_right), \ + p2i(&(n)->_tree_node), \ p2i((n)->_next), \ (n)->_word_size @@ -74,15 +70,6 @@ const size_t BlockTree::MinWordSize; #define tree_assert_invalid_node(cond, failure_node) \ tree_assert(cond, "Invalid node: " NODE_FORMAT, NODE_FORMAT_ARGS(failure_node)) -// walkinfo keeps a node plus the size corridor it and its children -// are supposed to be in. -struct BlockTree::walkinfo { - BlockTree::Node* n; - int depth; - size_t lim1; // ( - size_t lim2; // ) -}; - // Helper for verify() void BlockTree::verify_node_pointer(const Node* n) const { tree_assert(os::is_readable_pointer(n), @@ -98,80 +85,32 @@ void BlockTree::verify_node_pointer(const Node* n) const { void BlockTree::verify() const { // Traverse the tree and test that all nodes are in the correct order. - MemRangeCounter counter; - if (_root != nullptr) { - ResourceMark rm; - GrowableArray stack; + // Verifies node ordering (n1 < n2 => word_size1 < word_size2), + // node validity, and that the tree is balanced and not ill-formed. + _tree.verify_self([&](const TreeNode* tree_node) { + const Node* n = Node::cast_to_node(tree_node); - walkinfo info; - info.n = _root; - info.lim1 = 0; - info.lim2 = SIZE_MAX; - info.depth = 0; + verify_node_pointer(n); - stack.push(info); + counter.add(n->_word_size); - while (stack.length() > 0) { - info = stack.pop(); - const Node* n = info.n; + tree_assert_invalid_node(n->_word_size >= MinWordSize, n); + tree_assert_invalid_node(n->_word_size <= chunklevel::MAX_CHUNK_WORD_SIZE, n); - verify_node_pointer(n); - - // Assume a (ridiculously large) edge limit to catch cases - // of badly degenerated or circular trees. - tree_assert(info.depth < 10000, "too deep (%d)", info.depth); - counter.add(n->_word_size); - - if (n == _root) { - tree_assert_invalid_node(n->_parent == nullptr, n); - } else { - tree_assert_invalid_node(n->_parent != nullptr, n); - } - - // check size and ordering - tree_assert_invalid_node(n->_word_size >= MinWordSize, n); - tree_assert_invalid_node(n->_word_size <= chunklevel::MAX_CHUNK_WORD_SIZE, n); - tree_assert_invalid_node(n->_word_size > info.lim1, n); - tree_assert_invalid_node(n->_word_size < info.lim2, n); - - // Check children - if (n->_left != nullptr) { - tree_assert_invalid_node(n->_left != n, n); - tree_assert_invalid_node(n->_left->_parent == n, n); - - walkinfo info2; - info2.n = n->_left; - info2.lim1 = info.lim1; - info2.lim2 = n->_word_size; - info2.depth = info.depth + 1; - stack.push(info2); - } - - if (n->_right != nullptr) { - tree_assert_invalid_node(n->_right != n, n); - tree_assert_invalid_node(n->_right->_parent == n, n); - - walkinfo info2; - info2.n = n->_right; - info2.lim1 = n->_word_size; - info2.lim2 = info.lim2; - info2.depth = info.depth + 1; - stack.push(info2); - } - - // If node has same-sized siblings check those too. - const Node* n2 = n->_next; - while (n2 != nullptr) { - verify_node_pointer(n2); - tree_assert_invalid_node(n2 != n, n2); // catch simple circles - tree_assert_invalid_node(n2->_word_size == n->_word_size, n2); - counter.add(n2->_word_size); - n2 = n2->_next; - } + // If node has same-sized siblings check those too. + const Node* n2 = n->_next; + while (n2 != nullptr) { + verify_node_pointer(n2); + tree_assert_invalid_node(n2 != n, n2); // catch simple circles + tree_assert_invalid_node(n2->_word_size == n->_word_size, n2); + counter.add(n2->_word_size); + n2 = n2->_next; } - } + + return true; + }); // At the end, check that counters match // (which also verifies that we visited every node, or at least @@ -189,64 +128,34 @@ void BlockTree::print_tree(outputStream* st) const { // as a quasi list is much clearer to the eye. // We print the tree depth-first, with stacked nodes below normal ones // (normal "real" nodes are marked with a leading '+') - if (_root != nullptr) { + if (is_empty()) { + st->print_cr(""); + return; + } - ResourceMark rm; - GrowableArray stack; + _tree.print_on(st, [&](outputStream *st, const TreeNode *tree_node, int depth) { + const Node* n = Node::cast_to_node(tree_node); - walkinfo info; - info.n = _root; - info.depth = 0; - - stack.push(info); - while (stack.length() > 0) { - info = stack.pop(); - const Node* n = info.n; - - // Print node. - st->print("%4d + ", info.depth); - if (os::is_readable_pointer(n)) { - st->print_cr(NODE_FORMAT, NODE_FORMAT_ARGS(n)); - } else { - st->print_cr("@" PTR_FORMAT ": unreadable (skipping subtree)", p2i(n)); - continue; // don't print this subtree - } - - // Print same-sized-nodes stacked under this node - for (Node* n2 = n->_next; n2 != nullptr; n2 = n2->_next) { - st->print_raw(" "); - if (os::is_readable_pointer(n2)) { - st->print_cr(NODE_FORMAT, NODE_FORMAT_ARGS(n2)); - } else { - st->print_cr("@" PTR_FORMAT ": unreadable (skipping rest of chain).", p2i(n2)); - break; // stop printing this chain. - } - } - - // Handle simple circularities - if (n == n->_right || n == n->_left || n == n->_next) { - st->print_cr("@" PTR_FORMAT ": circularity detected.", p2i(n)); - return; // stop printing - } - - // Handle children. - if (n->_right != nullptr) { - walkinfo info2; - info2.n = n->_right; - info2.depth = info.depth + 1; - stack.push(info2); - } - if (n->_left != nullptr) { - walkinfo info2; - info2.n = n->_left; - info2.depth = info.depth + 1; - stack.push(info2); - } + // Print node. + st->print("%4d + ", depth); + if (os::is_readable_pointer(n)) { + st->print_cr(NODE_FORMAT, NODE_FORMAT_ARGS(n)); + } else { + st->print_cr("@" PTR_FORMAT ": unreadable", p2i(n)); + return; } - } else { - st->print_cr(""); - } + // Print same-sized-nodes stacked under this node + for (Node* n2 = n->_next; n2 != nullptr; n2 = n2->_next) { + st->print_raw(" "); + if (os::is_readable_pointer(n2)) { + st->print_cr(NODE_FORMAT, NODE_FORMAT_ARGS(n2)); + } else { + st->print_cr("@" PTR_FORMAT ": unreadable (skipping rest of chain).", p2i(n2)); + break; // stop printing this chain. + } + } + }); } #endif // ASSERT diff --git a/src/hotspot/share/memory/metaspace/blockTree.hpp b/src/hotspot/share/memory/metaspace/blockTree.hpp index a01f60b166f..e7c1edf9c4f 100644 --- a/src/hotspot/share/memory/metaspace/blockTree.hpp +++ b/src/hotspot/share/memory/metaspace/blockTree.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -32,17 +32,18 @@ #include "memory/metaspace/metablock.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/rbTree.inline.hpp" namespace metaspace { -// BlockTree is a rather simple binary search tree. It is used to -// manage medium to large free memory blocks. +// BlockTree is tree built on an intrusive red-black tree. +// It is used to manage medium to large free memory blocks. // // There is no separation between payload (managed blocks) and nodes: the // memory blocks themselves are the nodes, with the block size being the key. // // We store node pointer information in these blocks when storing them. That -// imposes a minimum size to the managed memory blocks (1 word) +// imposes a minimum size to the managed memory blocks (1 MinWordSize) // // We want to manage many memory blocks of the same size, but we want // to prevent the tree from blowing up and degenerating into a list. Therefore @@ -53,9 +54,9 @@ namespace metaspace { // | 100 | // +-----+ // / \ -// +-----+ -// | 80 | -// +-----+ +// +-----+ +-----+ +// | 80 | | 120 | +// +-----+ +-----+ // / | \ // / +-----+ \ // +-----+ | 80 | +-----+ @@ -65,16 +66,11 @@ namespace metaspace { // | 80 | // +-----+ // -// -// Todo: This tree is unbalanced. It would be a good fit for a red-black tree. -// In order to make this a red-black tree, we need an algorithm which can deal -// with nodes which are their own payload (most red-black tree implementations -// swap payloads of their nodes at some point, see e.g. j.u.TreeSet). -// A good example is the Linux kernel rbtree, which is a clean, easy-to-read -// implementation. class BlockTree: public CHeapObj { + using TreeNode = IntrusiveRBNode; + struct Node { static const intptr_t _canary_value = @@ -86,29 +82,27 @@ class BlockTree: public CHeapObj { // in debug. const intptr_t _canary; - // Normal tree node stuff... - // (Note: all null if this is a stacked node) - Node* _parent; - Node* _left; - Node* _right; + // Tree node for linking blocks in the intrusive tree. + TreeNode _tree_node; // Blocks with the same size are put in a list with this node as head. Node* _next; // Word size of node. Note that size cannot be larger than max metaspace size, - // so this could be very well a 32bit value (in case we ever make this a balancing - // tree and need additional space for weighting information). + // so this could very well be a 32bit value. const size_t _word_size; Node(size_t word_size) : _canary(_canary_value), - _parent(nullptr), - _left(nullptr), - _right(nullptr), + _tree_node{}, _next(nullptr), _word_size(word_size) {} + static Node* cast_to_node(const TreeNode* tree_node) { + return (Node*)((uintptr_t)tree_node - offset_of(Node, _tree_node)); + } + #ifdef ASSERT bool valid() const { return _canary == _canary_value && @@ -118,8 +112,23 @@ class BlockTree: public CHeapObj { #endif }; - // Needed for verify() and print_tree() - struct walkinfo; + struct TreeComparator { + static RBTreeOrdering cmp(const size_t a, const TreeNode* b) { + const size_t node_word_size = Node::cast_to_node(b)->_word_size; + + if (a < node_word_size) { return RBTreeOrdering::LT; } + if (a > node_word_size) { return RBTreeOrdering::GT; } + return RBTreeOrdering::EQ; + } + + static bool less_than(const TreeNode* a, const TreeNode* b) { + const size_t a_word_size = Node::cast_to_node(a)->_word_size; + const size_t b_word_size = Node::cast_to_node(b)->_word_size; + + if (a_word_size < b_word_size) { return true; } + return false; + } + }; #ifdef ASSERT // Run a quick check on a node; upon suspicion dive into a full tree check. @@ -134,7 +143,7 @@ public: private: - Node* _root; + IntrusiveRBTree _tree; MemRangeCounter _counter; @@ -143,7 +152,7 @@ private: assert(head->_word_size == n->_word_size, "sanity"); n->_next = head->_next; head->_next = n; - DEBUG_ONLY(n->_left = n->_right = n->_parent = nullptr;) + DEBUG_ONLY(n->_tree_node = TreeNode()); } // Given a node list starting at head, remove one of the follow up nodes from @@ -157,183 +166,6 @@ private: return n; } - // Given a node c and a node p, wire up c as left child of p. - static void set_left_child(Node* p, Node* c) { - p->_left = c; - if (c != nullptr) { - assert(c->_word_size < p->_word_size, "sanity"); - c->_parent = p; - } - } - - // Given a node c and a node p, wire up c as right child of p. - static void set_right_child(Node* p, Node* c) { - p->_right = c; - if (c != nullptr) { - assert(c->_word_size > p->_word_size, "sanity"); - c->_parent = p; - } - } - - // Given a node n, return its successor in the tree - // (node with the next-larger size). - static Node* successor(Node* n) { - Node* succ = nullptr; - if (n->_right != nullptr) { - // If there is a right child, search the left-most - // child of that child. - succ = n->_right; - while (succ->_left != nullptr) { - succ = succ->_left; - } - } else { - succ = n->_parent; - Node* n2 = n; - // As long as I am the right child of my parent, search upward - while (succ != nullptr && n2 == succ->_right) { - n2 = succ; - succ = succ->_parent; - } - } - return succ; - } - - // Given a node, replace it with a replacement node as a child for its parent. - // If the node is root and has no parent, sets it as root. - void replace_node_in_parent(Node* child, Node* replace) { - Node* parent = child->_parent; - if (parent != nullptr) { - if (parent->_left == child) { // Child is left child - set_left_child(parent, replace); - } else { - set_right_child(parent, replace); - } - } else { - assert(child == _root, "must be root"); - _root = replace; - if (replace != nullptr) { - replace->_parent = nullptr; - } - } - return; - } - - // Given a node n and an insertion point, insert n under insertion point. - void insert(Node* insertion_point, Node* n) { - assert(n->_parent == nullptr, "Sanity"); - for (;;) { - DEBUG_ONLY(check_node(insertion_point);) - if (n->_word_size == insertion_point->_word_size) { - add_to_list(n, insertion_point); // parent stays null in this case. - break; - } else if (n->_word_size > insertion_point->_word_size) { - if (insertion_point->_right == nullptr) { - set_right_child(insertion_point, n); - break; - } else { - insertion_point = insertion_point->_right; - } - } else { - if (insertion_point->_left == nullptr) { - set_left_child(insertion_point, n); - break; - } else { - insertion_point = insertion_point->_left; - } - } - } - } - - // Given a node and a wish size, search this node and all children for - // the node closest (equal or larger sized) to the size s. - Node* find_closest_fit(Node* n, size_t s) { - Node* best_match = nullptr; - while (n != nullptr) { - DEBUG_ONLY(check_node(n);) - if (n->_word_size >= s) { - best_match = n; - if (n->_word_size == s) { - break; // perfect match or max depth reached - } - n = n->_left; - } else { - n = n->_right; - } - } - return best_match; - } - - // Given a wish size, search the whole tree for a - // node closest (equal or larger sized) to the size s. - Node* find_closest_fit(size_t s) { - if (_root != nullptr) { - return find_closest_fit(_root, s); - } - return nullptr; - } - - // Given a node n, remove it from the tree and repair tree. - void remove_node_from_tree(Node* n) { - assert(n->_next == nullptr, "do not delete a node which has a non-empty list"); - - if (n->_left == nullptr && n->_right == nullptr) { - replace_node_in_parent(n, nullptr); - - } else if (n->_left == nullptr && n->_right != nullptr) { - replace_node_in_parent(n, n->_right); - - } else if (n->_left != nullptr && n->_right == nullptr) { - replace_node_in_parent(n, n->_left); - - } else { - // Node has two children. - - // 1) Find direct successor (the next larger node). - Node* succ = successor(n); - - // There has to be a successor since n->right was != null... - assert(succ != nullptr, "must be"); - - // ... and it should not have a left child since successor - // is supposed to be the next larger node, so it must be the mostleft node - // in the sub tree rooted at n->right - assert(succ->_left == nullptr, "must be"); - assert(succ->_word_size > n->_word_size, "sanity"); - - Node* successor_parent = succ->_parent; - Node* successor_right_child = succ->_right; - - // Remove successor from its parent. - if (successor_parent == n) { - - // special case: successor is a direct child of n. Has to be the right child then. - assert(n->_right == succ, "sanity"); - - // Just replace n with this successor. - replace_node_in_parent(n, succ); - - // Take over n's old left child, too. - // We keep the successor's right child. - set_left_child(succ, n->_left); - } else { - // If the successors parent is not n, we are deeper in the tree, - // the successor has to be the left child of its parent. - assert(successor_parent->_left == succ, "sanity"); - - // The right child of the successor (if there was one) replaces - // the successor at its parent's left child. - set_left_child(successor_parent, succ->_right); - - // and the successor replaces n at its parent - replace_node_in_parent(n, succ); - - // and takes over n's old children - set_left_child(succ, n->_left); - set_right_child(succ, n->_right); - } - } - } - #ifdef ASSERT void zap_block(MetaBlock block); // Helper for verify() @@ -342,7 +174,7 @@ private: public: - BlockTree() : _root(nullptr) {} + BlockTree() {} // Add a memory block to the tree. Its content will be overwritten. void add_block(MetaBlock block) { @@ -350,10 +182,12 @@ public: const size_t word_size = block.word_size(); assert(word_size >= MinWordSize, "invalid block size %zu", word_size); Node* n = new(block.base()) Node(word_size); - if (_root == nullptr) { - _root = n; - } else { - insert(_root, n); + IntrusiveRBTree::Cursor cursor = _tree.cursor(word_size); + if (cursor.found()) { + add_to_list(n, Node::cast_to_node(cursor.node())); + } + else { + _tree.insert_at_cursor(&n->_tree_node, cursor); } _counter.add(word_size); } @@ -364,9 +198,10 @@ public: assert(word_size >= MinWordSize, "invalid block size %zu", word_size); MetaBlock result; - Node* n = find_closest_fit(word_size); + TreeNode* tree_node = _tree.closest_ge(word_size); - if (n != nullptr) { + if (tree_node != nullptr) { + Node* n = Node::cast_to_node(tree_node); DEBUG_ONLY(check_node(n);) assert(n->_word_size >= word_size, "sanity"); @@ -377,7 +212,7 @@ public: // node into its place in the tree). n = remove_from_list(n); } else { - remove_node_from_tree(n); + _tree.remove(tree_node); } result = MetaBlock((MetaWord*)n, n->_word_size); @@ -395,7 +230,7 @@ public: // Returns total size, in words, of all elements. size_t total_size() const { return _counter.total_size(); } - bool is_empty() const { return _root == nullptr; } + bool is_empty() const { return _tree.size() == 0; } DEBUG_ONLY(void print_tree(outputStream* st) const;) DEBUG_ONLY(void verify() const;) diff --git a/src/hotspot/share/memory/metaspaceClosure.cpp b/src/hotspot/share/memory/metaspaceClosure.cpp index f3c6e350034..0239eadf692 100644 --- a/src/hotspot/share/memory/metaspaceClosure.cpp +++ b/src/hotspot/share/memory/metaspaceClosure.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -22,7 +22,21 @@ * */ +#include "cds/aotGrowableArray.hpp" +#include "classfile/packageEntry.hpp" #include "memory/metaspaceClosure.hpp" +#include "oops/array.hpp" +#include "oops/instanceKlass.hpp" + +// Sanity checks +static_assert(!HAS_METASPACE_POINTERS_DO(int)); + +static_assert(HAS_METASPACE_POINTERS_DO(Array)); +static_assert(HAS_METASPACE_POINTERS_DO(Array)); +static_assert(HAS_METASPACE_POINTERS_DO(InstanceKlass)); +static_assert(HAS_METASPACE_POINTERS_DO(PackageEntry)); +static_assert(HAS_METASPACE_POINTERS_DO(AOTGrowableArray)); +static_assert(HAS_METASPACE_POINTERS_DO(AOTGrowableArray)); void MetaspaceClosure::push_impl(MetaspaceClosure::Ref* ref) { if (_enclosing_ref != nullptr) { diff --git a/src/hotspot/share/memory/metaspaceClosure.hpp b/src/hotspot/share/memory/metaspaceClosure.hpp index 329502d7a47..b6ba69d6f63 100644 --- a/src/hotspot/share/memory/metaspaceClosure.hpp +++ b/src/hotspot/share/memory/metaspaceClosure.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -25,9 +25,11 @@ #ifndef SHARE_MEMORY_METASPACECLOSURE_HPP #define SHARE_MEMORY_METASPACECLOSURE_HPP +#include "cds/aotGrowableArray.hpp" #include "cppstdlib/type_traits.hpp" #include "logging/log.hpp" #include "memory/allocation.hpp" +#include "memory/metaspaceClosureType.hpp" #include "metaprogramming/enableIf.hpp" #include "oops/array.hpp" #include "utilities/debug.hpp" @@ -36,41 +38,27 @@ #include "utilities/macros.hpp" #include "utilities/resizableHashTable.hpp" -// The metadata hierarchy is separate from the oop hierarchy - class MetaspaceObj; // no C++ vtable -//class Array; // no C++ vtable - class Annotations; // no C++ vtable - class ConstantPoolCache; // no C++ vtable - class ConstMethod; // no C++ vtable - class MethodCounters; // no C++ vtable - class Symbol; // no C++ vtable - class Metadata; // has C++ vtable (so do all subclasses) - class ConstantPool; - class MethodData; - class Method; - class Klass; - class InstanceKlass; - class InstanceMirrorKlass; - class InstanceClassLoaderKlass; - class InstanceRefKlass; - class ArrayKlass; - class ObjArrayKlass; - class TypeArrayKlass; +// This macro just check for the existence of a member with the name "metaspace_pointers_do". If the +// parameter list is not (MetaspaceClosure* it), you will get a compilation error. +#define HAS_METASPACE_POINTERS_DO(T) HasMetaspacePointersDo::value + +template +class HasMetaspacePointersDo { + template static void* test(decltype(&U::metaspace_pointers_do)); + template static int test(...); + using test_type = decltype(test(nullptr)); +public: + static constexpr bool value = std::is_pointer_v; +}; // class MetaspaceClosure -- // -// This class is used for iterating the objects in the HotSpot Metaspaces. It +// This class is used for iterating the class metadata objects. It // provides an API to walk all the reachable objects starting from a set of // root references (such as all Klass'es in the SystemDictionary). // -// Currently it is used for compacting the CDS archive by eliminate temporary -// objects allocated during archive creation time. See ArchiveBuilder for an example. -// -// To support MetaspaceClosure, each subclass of MetaspaceObj must provide -// a method of the type void metaspace_pointers_do(MetaspaceClosure*). This method -// should call MetaspaceClosure::push() on every pointer fields of this -// class that points to a MetaspaceObj. See Annotations::metaspace_pointers_do() -// for an example. +// Currently it is used to copy class metadata into the AOT cache. +// See ArchiveBuilder for an example. class MetaspaceClosure { public: enum Writability { @@ -79,16 +67,32 @@ public: _default }; +#define METASPACE_CLOSURE_TYPE_NAME_CASE(name) case MetaspaceClosureType::name ## Type: return #name; + + static const char* type_name(MetaspaceClosureType type) { + switch(type) { + METASPACE_CLOSURE_TYPES_DO(METASPACE_CLOSURE_TYPE_NAME_CASE) + default: + ShouldNotReachHere(); + return nullptr; + } + } + // class MetaspaceClosure::Ref -- // - // MetaspaceClosure can be viewed as a very simple type of copying garbage - // collector. For it to function properly, it requires each subclass of - // MetaspaceObj to provide two methods: + // For type X to be iterable by MetaspaceClosure, X (or one of X's supertypes) must have + // the following public functions: + // void metaspace_pointers_do(MetaspaceClosure* it); + // static bool is_read_only_by_default() { return true; } // - // size_t size(); -- to determine how much data to copy - // void metaspace_pointers_do(MetaspaceClosure*); -- to locate all the embedded pointers + // In addition, if X is not a subtype of MetaspaceObj, it must have the following function: + // MetaspaceClosureType type() const; + // int size_in_heapwords() const; // - // Calling these methods would be trivial if these two were virtual methods. + // Currently, the iterable types include all subtypes of MetsapceObj, as well + // as GrowableArray, ModuleEntry and PackageEntry. + // + // Calling these functions would be trivial if these were virtual functions. // However, to save space, MetaspaceObj has NO vtable. The vtable is introduced // only in the Metadata class. // @@ -97,7 +101,7 @@ public: // so that we can statically discover the type of a object. The use of Ref // depends on the fact that: // - // [1] We don't use polymorphic pointers for MetaspaceObj's that are not subclasses + // [1] We don't use polymorphic pointers to MetaspaceObj's that are not subclasses // of Metadata. I.e., we don't do this: // class Klass { // MetaspaceObj *_obj; @@ -130,8 +134,7 @@ public: virtual bool not_null() const = 0; virtual int size() const = 0; virtual void metaspace_pointers_do(MetaspaceClosure *it) const = 0; - virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const = 0; - virtual MetaspaceObj::Type msotype() const = 0; + virtual MetaspaceClosureType type() const = 0; virtual bool is_read_only_by_default() const = 0; virtual ~Ref() {} @@ -180,7 +183,26 @@ public: } private: - // MSORef -- iterate an instance of MetaspaceObj + template ::value)> + static int get_size(T* obj) { + return obj->size(); + } + + template ::value)> + static int get_size(T* obj) { + return obj->size_in_heapwords(); + } + + static MetaspaceClosureType as_type(MetaspaceClosureType t) { + return t; + } + + static MetaspaceClosureType as_type(MetaspaceObj::Type msotype) { + precond(msotype < MetaspaceObj::_number_of_types); + return (MetaspaceClosureType)msotype; + } + + // MSORef -- iterate an instance of T, where T::metaspace_pointers_do() exists. template class MSORef : public Ref { T** _mpp; T* dereference() const { @@ -196,18 +218,20 @@ private: virtual bool is_read_only_by_default() const { return T::is_read_only_by_default(); } virtual bool not_null() const { return dereference() != nullptr; } - virtual int size() const { return dereference()->size(); } - virtual MetaspaceObj::Type msotype() const { return dereference()->type(); } + virtual int size() const { return get_size(dereference()); } + virtual MetaspaceClosureType type() const { return as_type(dereference()->type()); } virtual void metaspace_pointers_do(MetaspaceClosure *it) const { dereference()->metaspace_pointers_do(it); } - virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { - ((T*)new_loc)->metaspace_pointers_do(it); - } }; - // abstract base class for MSOArrayRef, MSOPointerArrayRef and OtherArrayRef + //--------------------- + // Support for Array + //--------------------- + + // Abstract base class for MSOArrayRef, MSOPointerArrayRef and OtherArrayRef. + // These are used for iterating Array. template class ArrayRef : public Ref { Array** _mpp; protected: @@ -224,10 +248,10 @@ private: virtual bool is_read_only_by_default() const { return true; } virtual bool not_null() const { return dereference() != nullptr; } virtual int size() const { return dereference()->size(); } - virtual MetaspaceObj::Type msotype() const { return MetaspaceObj::array_type(sizeof(T)); } + virtual MetaspaceClosureType type() const { return as_type(MetaspaceObj::array_type(sizeof(T))); } }; - // OtherArrayRef -- iterate an instance of Array, where T is NOT a subtype of MetaspaceObj. + // OtherArrayRef -- iterate an instance of Array, where T does NOT have metaspace_pointer_do(). // T can be a primitive type, such as int, or a structure. However, we do not scan // the fields inside T, so you should not embed any pointers inside T. template class OtherArrayRef : public ArrayRef { @@ -238,13 +262,9 @@ private: Array* array = ArrayRef::dereference(); log_trace(aot)("Iter(OtherArray): %p [%d]", array, array->length()); } - virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { - Array* array = (Array*)new_loc; - log_trace(aot)("Iter(OtherArray): %p [%d]", array, array->length()); - } }; - // MSOArrayRef -- iterate an instance of Array, where T is a subtype of MetaspaceObj. + // MSOArrayRef -- iterate an instance of Array, where T has metaspace_pointer_do(). // We recursively call T::metaspace_pointers_do() for each element in this array. template class MSOArrayRef : public ArrayRef { public: @@ -253,9 +273,6 @@ private: virtual void metaspace_pointers_do(MetaspaceClosure *it) const { metaspace_pointers_do_at_impl(it, ArrayRef::dereference()); } - virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { - metaspace_pointers_do_at_impl(it, (Array*)new_loc); - } private: void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array* array) const { log_trace(aot)("Iter(MSOArray): %p [%d]", array, array->length()); @@ -266,7 +283,7 @@ private: } }; - // MSOPointerArrayRef -- iterate an instance of Array, where T is a subtype of MetaspaceObj. + // MSOPointerArrayRef -- iterate an instance of Array, where T has metaspace_pointer_do(). // We recursively call MetaspaceClosure::push() for each pointer in this array. template class MSOPointerArrayRef : public ArrayRef { public: @@ -275,9 +292,6 @@ private: virtual void metaspace_pointers_do(MetaspaceClosure *it) const { metaspace_pointers_do_at_impl(it, ArrayRef::dereference()); } - virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { - metaspace_pointers_do_at_impl(it, (Array*)new_loc); - } private: void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array* array) const { log_trace(aot)("Iter(MSOPointerArray): %p [%d]", array, array->length()); @@ -288,6 +302,102 @@ private: } }; + //-------------------------------- + // Support for AOTGrowableArray + //-------------------------------- + + // Abstract base class for MSOCArrayRef, MSOPointerCArrayRef and OtherCArrayRef. + // These are used for iterating the buffer held by AOTGrowableArray. + template class CArrayRef : public Ref { + T** _mpp; + int _num_elems; // Number of elements + + int byte_size() const { + return _num_elems * sizeof(T); + } + + protected: + // C pointer arrays don't support tagged pointers. + T* dereference() const { + return *_mpp; + } + virtual void** mpp() const { + return (void**)_mpp; + } + int num_elems() const { + return _num_elems; + } + public: + CArrayRef(T** mpp, int num_elems, Writability w) + : Ref(w), _mpp(mpp), _num_elems(num_elems) { + assert(is_aligned(byte_size(), BytesPerWord), "must be"); + } + + virtual bool is_read_only_by_default() const { return false; } + virtual bool not_null() const { return dereference() != nullptr; } + virtual int size() const { return (int)heap_word_size(byte_size()); } + virtual MetaspaceClosureType type() const { return MetaspaceClosureType::CArrayType; } + }; + + // OtherCArrayRef -- iterate a C array of type T, where T does NOT have metaspace_pointer_do(). + // T can be a primitive type, such as int, or a structure. However, we do not scan + // the fields inside T, so you should not embed any pointers inside T. + template class OtherCArrayRef : public CArrayRef { + public: + OtherCArrayRef(T** mpp, int num_elems, Writability w) : CArrayRef(mpp, num_elems, w) {} + + virtual void metaspace_pointers_do(MetaspaceClosure *it) const { + T* array = CArrayRef::dereference(); + log_trace(aot)("Iter(OtherCArray): %p [%d]", array, CArrayRef::num_elems()); + } + }; + + // MSOCArrayRef -- iterate a C array of type T, where T has metaspace_pointer_do(). + // We recursively call T::metaspace_pointers_do() for each element in this array. + // This is for supporting AOTGrowableArray. + // + // E.g., PackageEntry* _pkg_entry_pointers[2]; // a buffer that has 2 PackageEntry objects + // ... + // it->push(&_pkg_entry_pointers, 2); + // /* calls _pkg_entry_pointers[0].metaspace_pointers_do(it); */ + // /* calls _pkg_entry_pointers[1].metaspace_pointers_do(it); */ + template class MSOCArrayRef : public CArrayRef { + public: + MSOCArrayRef(T** mpp, int num_elems, Writability w) : CArrayRef(mpp, num_elems, w) {} + + virtual void metaspace_pointers_do(MetaspaceClosure *it) const { + T* array = CArrayRef::dereference(); + log_trace(aot)("Iter(MSOCArray): %p [%d]", array, CArrayRef::num_elems()); + for (int i = 0; i < CArrayRef::num_elems(); i++) { + T* elm = array + i; + elm->metaspace_pointers_do(it); + } + } + }; + + // MSOPointerCArrayRef -- iterate a C array of type T*, where T has metaspace_pointer_do(). + // We recursively call MetaspaceClosure::push() for each pointer in this array. + // This is for supporting AOTGrowableArray. + // + // E.g., PackageEntry** _pkg_entry_pointers[2]; // a buffer that has 2 PackageEntry pointers + // ... + // it->push(&_pkg_entry_pointers, 2); + // /* calls _pkg_entry_pointers[0]->metaspace_pointers_do(it); */ + // /* calls _pkg_entry_pointers[1]->metaspace_pointers_do(it); */ + template class MSOPointerCArrayRef : public CArrayRef { + public: + MSOPointerCArrayRef(T*** mpp, int num_elems, Writability w) : CArrayRef(mpp, num_elems, w) {} + + virtual void metaspace_pointers_do(MetaspaceClosure *it) const { + T** array = CArrayRef::dereference(); + log_trace(aot)("Iter(MSOPointerCArray): %p [%d]", array, CArrayRef::num_elems()); + for (int i = 0; i < CArrayRef::num_elems(); i++) { + T** mpp = array + i; + it->push(mpp); + } + } + }; + // Normally, chains of references like a->b->c->d are iterated recursively. However, // if recursion is too deep, we save the Refs in _pending_refs, and push them later in // MetaspaceClosure::finish(). This avoids overflowing the C stack. @@ -330,37 +440,61 @@ public: // Array*>* a4 = ...; it->push(&a4); => MSOPointerArrayRef // Array* a5 = ...; it->push(&a5); => MSOPointerArrayRef // - // Note that the following will fail to compile (to prevent you from adding new fields - // into the MetaspaceObj subtypes that cannot be properly copied by CDS): + // AOTGrowableArrays have a separate "C array" buffer, so they are scanned in two steps: // - // MemoryPool* p = ...; it->push(&p); => MemoryPool is not a subclass of MetaspaceObj - // Array* a6 = ...; it->push(&a6); => MemoryPool is not a subclass of MetaspaceObj - // Array* a7 = ...; it->push(&a7); => int is not a subclass of MetaspaceObj + // AOTGrowableArray* ga1 = ...; it->push(&ga1); => MSORef => OtherCArrayRef + // AOTGrowableArray* ga2 = ...; it->push(&ga2); => MSORef => MSOCArrayRef + // AOTGrowableArray* ga3 = ...; it->push(&ga3); => MSORef => MSOPointerCArrayRef + // + // Note that the following will fail to compile: + // + // MemoryPool* p = ...; it->push(&p); => MemoryPool doesn't have metaspace_pointers_do + // Array* a6 = ...; it->push(&a6); => MemoryPool doesn't have metaspace_pointers_do + // Array* a7 = ...; it->push(&a7); => int doesn't have metaspace_pointers_do + // --- Regular iterable objects template void push(T** mpp, Writability w = _default) { - static_assert(std::is_base_of::value, "Do not push pointers of arbitrary types"); + static_assert(HAS_METASPACE_POINTERS_DO(T), "Do not push pointers of arbitrary types"); push_with_ref>(mpp, w); } - template ::value)> + // --- Array + template void push(Array** mpp, Writability w = _default) { push_with_ref>(mpp, w); } - template ::value)> + template void push(Array** mpp, Writability w = _default) { push_with_ref>(mpp, w); } template void push(Array** mpp, Writability w = _default) { - static_assert(std::is_base_of::value, "Do not push Arrays of arbitrary pointer types"); + static_assert(HAS_METASPACE_POINTERS_DO(T), "Do not push Arrays of arbitrary pointer types"); push_with_ref>(mpp, w); } + + // --- The buffer of AOTGrowableArray + template + void push_c_array(T** mpp, int num_elems, Writability w = _default) { + push_impl(new OtherCArrayRef(mpp, num_elems, w)); + } + + template + void push_c_array(T** mpp, int num_elems, Writability w = _default) { + push_impl(new MSOCArrayRef(mpp, num_elems, w)); + } + + template + void push_c_array(T*** mpp, int num_elems, Writability w = _default) { + static_assert(HAS_METASPACE_POINTERS_DO(T), "Do not push C arrays of arbitrary pointer types"); + push_impl(new MSOPointerCArrayRef(mpp, num_elems, w)); + } }; -// This is a special MetaspaceClosure that visits each unique MetaspaceObj once. +// This is a special MetaspaceClosure that visits each unique object once. class UniqueMetaspaceClosure : public MetaspaceClosure { static const int INITIAL_TABLE_SIZE = 15889; static const int MAX_TABLE_SIZE = 1000000; diff --git a/src/hotspot/share/memory/metaspaceClosureType.hpp b/src/hotspot/share/memory/metaspaceClosureType.hpp new file mode 100644 index 00000000000..adf6805521b --- /dev/null +++ b/src/hotspot/share/memory/metaspaceClosureType.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2026, 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. + * + * 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. + * + */ + +#ifndef SHARE_MEMORY_METASPACECLOSURETYPE_HPP +#define SHARE_MEMORY_METASPACECLOSURETYPE_HPP + +#include "memory/allocation.hpp" + +// MetaspaceClosure is able to iterate on MetaspaceObjs, plus the following classes +#define METASPACE_CLOSURE_TYPES_DO(f) \ + METASPACE_OBJ_TYPES_DO(f) \ + f(CArray) \ + f(GrowableArray) \ + f(ModuleEntry) \ + f(PackageEntry) \ + +#define METASPACE_CLOSURE_TYPE_DECLARE(name) name ## Type, + +enum class MetaspaceClosureType : int { + METASPACE_CLOSURE_TYPES_DO(METASPACE_CLOSURE_TYPE_DECLARE) + _number_of_types +}; + + +#endif // SHARE_MEMORY_METASPACECLOSURETYPE_HPP diff --git a/src/hotspot/share/oops/array.hpp b/src/hotspot/share/oops/array.hpp index 90dcc9dfeaf..82067e007ea 100644 --- a/src/hotspot/share/oops/array.hpp +++ b/src/hotspot/share/oops/array.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -33,6 +33,8 @@ // Array for metadata allocation +class MetaspaceClosure; + template class Array: public MetaspaceObj { friend class ArchiveBuilder; @@ -157,6 +159,9 @@ protected: st->print("Array(" PTR_FORMAT ")", p2i(this)); } + // This function does nothing. The iteration of the elements are done inside metaspaceClosure.hpp + void metaspace_pointers_do(MetaspaceClosure* it) {} + #ifndef PRODUCT void print(outputStream* st) { for (int i = 0; i< _length; i++) { diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 56a80daed60..1963327fc78 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -2693,6 +2693,10 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) { it->push(&_nest_members); it->push(&_permitted_subclasses); it->push(&_record_components); + + if (CDSConfig::is_dumping_full_module_graph() && !defined_by_other_loaders()) { + it->push(&_package_entry); + } } #if INCLUDE_CDS @@ -2791,24 +2795,9 @@ void InstanceKlass::remove_java_mirror() { void InstanceKlass::init_shared_package_entry() { assert(CDSConfig::is_dumping_archive(), "must be"); -#if !INCLUDE_CDS_JAVA_HEAP - _package_entry = nullptr; -#else - if (CDSConfig::is_dumping_full_module_graph()) { - if (defined_by_other_loaders()) { - _package_entry = nullptr; - } else { - _package_entry = PackageEntry::get_archived_entry(_package_entry); - } - } else if (CDSConfig::is_dumping_dynamic_archive() && - CDSConfig::is_using_full_module_graph() && - AOTMetaspace::in_aot_cache(_package_entry)) { - // _package_entry is an archived package in the base archive. Leave it as is. - } else { + if (!CDSConfig::is_dumping_full_module_graph() || defined_by_other_loaders()) { _package_entry = nullptr; } - ArchivePtrMarker::mark_pointer((address**)&_package_entry); -#endif } void InstanceKlass::compute_has_loops_flag_for_methods() { diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index 23a59d26093..e370a3b7a7c 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -353,6 +353,9 @@ class InstanceKlass: public Klass { int static_oop_field_count() const { return (int)_static_oop_field_count; } void set_static_oop_field_count(u2 size) { _static_oop_field_count = size; } + bool trust_final_fields() { return _misc_flags.trust_final_fields(); } + void set_trust_final_fields(bool value) { _misc_flags.set_trust_final_fields(value); } + // Java itable int itable_length() const { return _itable_len; } void set_itable_length(int len) { _itable_len = len; } diff --git a/src/hotspot/share/oops/instanceKlassFlags.hpp b/src/hotspot/share/oops/instanceKlassFlags.hpp index 5eebaab27d1..d576805b30f 100644 --- a/src/hotspot/share/oops/instanceKlassFlags.hpp +++ b/src/hotspot/share/oops/instanceKlassFlags.hpp @@ -54,6 +54,7 @@ class InstanceKlassFlags { flag(has_localvariable_table , 1 << 11) /* has localvariable information */ \ flag(has_miranda_methods , 1 << 12) /* True if this class has miranda methods in it's vtable */ \ flag(has_final_method , 1 << 13) /* True if klass has final method */ \ + flag(trust_final_fields , 1 << 14) /* All instance final fields in this class should be trusted */ \ /* end of list */ #define IK_FLAGS_ENUM_NAME(name, value) _misc_##name = value, diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp index b6974762209..d59db9744cb 100644 --- a/src/hotspot/share/oops/klass.hpp +++ b/src/hotspot/share/oops/klass.hpp @@ -510,18 +510,20 @@ protected: return (BasicType) btvalue; } - // Want a pattern to quickly diff against layout header in register - // find something less clever! + // Return a value containing a single set bit that is in the bitset difference between the + // layout helpers for array-of-boolean and array-of-byte. static int layout_helper_boolean_diffbit() { - jint zlh = array_layout_helper(T_BOOLEAN); - jint blh = array_layout_helper(T_BYTE); - assert(zlh != blh, "array layout helpers must differ"); - int diffbit = 1; - while ((diffbit & (zlh ^ blh)) == 0 && (diffbit & zlh) == 0) { - diffbit <<= 1; - assert(diffbit != 0, "make sure T_BOOLEAN has a different bit than T_BYTE"); - } - return diffbit; + uint zlh = static_cast(array_layout_helper(T_BOOLEAN)); + uint blh = static_cast(array_layout_helper(T_BYTE)); + // get all the bits that are set in zlh and clear in blh + uint candidates = (zlh & ~blh); + assert(candidates != 0, "must be"); // must be some if there is a solution. + // Use well known bit hack to isolate the low bit of candidates. + uint result = candidates & (-candidates); + assert(is_power_of_2(result), "must be power of 2"); + assert((result & zlh) != 0, "must be set in alh of T_BOOLEAN"); + assert((result & blh) == 0, "must be clear in alh of T_BYTE"); + return static_cast(result); } static int layout_helper_log2_element_size(jint lh) { diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index cea2b09e142..c4c9445b61a 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -581,7 +581,7 @@ void Compile::print_phase(const char* phase_name) { tty->print_cr("%u.\t%s", ++_phase_counter, phase_name); } -void Compile::print_ideal_ir(const char* phase_name) { +void Compile::print_ideal_ir(const char* compile_phase_name) const { // keep the following output all in one block // This output goes directly to the tty, not the compiler log. // To enable tools to match it up with the compilation activity, @@ -593,7 +593,7 @@ void Compile::print_ideal_ir(const char* phase_name) { stringStream ss; if (_output == nullptr) { - ss.print_cr("AFTER: %s", phase_name); + ss.print_cr("AFTER: %s", compile_phase_name); // Print out all nodes in ascending order of index. // It is important that we traverse both inputs and outputs of nodes, // so that we reach all nodes that are connected to Root. @@ -610,7 +610,7 @@ void Compile::print_ideal_ir(const char* phase_name) { xtty->head("ideal compile_id='%d'%s compile_phase='%s'", compile_id(), is_osr_compilation() ? " compile_kind='osr'" : "", - phase_name); + compile_phase_name); } tty->print("%s", ss.as_string()); @@ -671,7 +671,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, _coarsened_locks(comp_arena(), 8, 0, nullptr), _congraph(nullptr), NOT_PRODUCT(_igv_printer(nullptr) COMMA) - _unique(0), + _unique(0), _dead_node_count(0), _dead_node_list(comp_arena()), _node_arena_one(mtCompiler, Arena::Tag::tag_node), @@ -865,7 +865,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, #ifndef PRODUCT if (should_print_ideal()) { - print_ideal_ir("print_ideal"); + print_ideal_ir("PrintIdeal"); } #endif @@ -938,7 +938,7 @@ Compile::Compile(ciEnv* ci_env, _for_merge_stores_igvn(comp_arena(), 8, 0, nullptr), _congraph(nullptr), NOT_PRODUCT(_igv_printer(nullptr) COMMA) - _unique(0), + _unique(0), _dead_node_count(0), _dead_node_list(comp_arena()), _node_arena_one(mtCompiler, Arena::Tag::tag_node), @@ -1876,7 +1876,7 @@ void Compile::process_for_post_loop_opts_igvn(PhaseIterGVN& igvn) { // at least to this point, even if no loop optimizations were done. PhaseIdealLoop::verify(igvn); - if (has_loops() || _loop_opts_cnt > 0) { + if (_print_phase_loop_opts) { print_method(PHASE_AFTER_LOOP_OPTS, 2); } C->set_post_loop_opts_phase(); // no more loop opts allowed @@ -2098,6 +2098,7 @@ void Compile::inline_boxing_calls(PhaseIterGVN& igvn) { bool Compile::inline_incrementally_one() { assert(IncrementalInline, "incremental inlining should be on"); + assert(_late_inlines.length() > 0, "should have been checked by caller"); TracePhase tp(_t_incrInline_inline); @@ -2209,6 +2210,10 @@ void Compile::inline_incrementally(PhaseIterGVN& igvn) { igvn_worklist()->ensure_empty(); // should be done with igvn + if (_late_inlines.length() == 0) { + break; // no more progress + } + while (inline_incrementally_one()) { assert(!failing_internal() || failure_is_artificial(), "inconsistent"); } @@ -2219,10 +2224,6 @@ void Compile::inline_incrementally(PhaseIterGVN& igvn) { print_method(PHASE_INCREMENTAL_INLINE_STEP, 3); if (failing()) return; - - if (_late_inlines.length() == 0) { - break; // no more progress - } } igvn_worklist()->ensure_empty(); // should be done with igvn @@ -2404,7 +2405,8 @@ void Compile::Optimize() { if (failing()) return; - if (has_loops()) { + _print_phase_loop_opts = has_loops(); + if (_print_phase_loop_opts) { print_method(PHASE_BEFORE_LOOP_OPTS, 2); } @@ -5165,17 +5167,17 @@ void Compile::sort_macro_nodes() { } } -void Compile::print_method(CompilerPhaseType cpt, int level, Node* n) { +void Compile::print_method(CompilerPhaseType compile_phase, int level, Node* n) { if (failing_internal()) { return; } // failing_internal to not stress bailouts from printing code. EventCompilerPhase event(UNTIMED); if (event.should_commit()) { - CompilerEvent::PhaseEvent::post(event, C->_latest_stage_start_counter, cpt, C->_compile_id, level); + CompilerEvent::PhaseEvent::post(event, C->_latest_stage_start_counter, compile_phase, C->_compile_id, level); } #ifndef PRODUCT ResourceMark rm; stringStream ss; - ss.print_raw(CompilerPhaseTypeHelper::to_description(cpt)); - int iter = ++_igv_phase_iter[cpt]; + ss.print_raw(CompilerPhaseTypeHelper::to_description(compile_phase)); + int iter = ++_igv_phase_iter[compile_phase]; if (iter > 1) { ss.print(" %d", iter); } @@ -5203,8 +5205,8 @@ void Compile::print_method(CompilerPhaseType cpt, int level, Node* n) { if (should_print_phase(level)) { print_phase(name); } - if (should_print_ideal_phase(cpt)) { - print_ideal_ir(CompilerPhaseTypeHelper::to_name(cpt)); + if (should_print_ideal_phase(compile_phase)) { + print_ideal_ir(CompilerPhaseTypeHelper::to_name(compile_phase)); } #endif C->_latest_stage_start_counter.stamp(); diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 45a3a4f548f..d9a8ea15724 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -355,6 +355,7 @@ class Compile : public Phase { bool _print_assembly; // True if we should dump assembly code for this compilation bool _print_inlining; // True if we should print inlining for this compilation bool _print_intrinsics; // True if we should print intrinsics for this compilation + bool _print_phase_loop_opts; // True if we should print before and after loop opts phase #ifndef PRODUCT uint _phase_counter; // Counter for the number of already printed phases uint _igv_idx; // Counter for IGV node identifiers @@ -666,7 +667,7 @@ public: uint next_igv_idx() { return _igv_idx++; } bool trace_opto_output() const { return _trace_opto_output; } void print_phase(const char* phase_name); - void print_ideal_ir(const char* phase_name); + void print_ideal_ir(const char* compile_phase_name) const; bool should_print_ideal() const { return _directive->PrintIdealOption; } bool parsed_irreducible_loop() const { return _parsed_irreducible_loop; } void set_parsed_irreducible_loop(bool z) { _parsed_irreducible_loop = z; } @@ -680,7 +681,7 @@ public: void begin_method(); void end_method(); - void print_method(CompilerPhaseType cpt, int level, Node* n = nullptr); + void print_method(CompilerPhaseType compile_phase, int level, Node* n = nullptr); #ifndef PRODUCT bool should_print_igv(int level); diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 357c91e8eb5..b46a12fcf89 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -3511,10 +3511,7 @@ bool ConnectionGraph::is_oop_field(Node* n, int offset, bool* unsafe) { bt = field->layout_type(); } else { // Check for unsafe oop field access - if (n->has_out_with(Op_StoreP, Op_LoadP, Op_StoreN, Op_LoadN) || - n->has_out_with(Op_GetAndSetP, Op_GetAndSetN, Op_CompareAndExchangeP, Op_CompareAndExchangeN) || - n->has_out_with(Op_CompareAndSwapP, Op_CompareAndSwapN, Op_WeakCompareAndSwapP, Op_WeakCompareAndSwapN) || - BarrierSet::barrier_set()->barrier_set_c2()->escape_has_out_with_unsafe_object(n)) { + if (has_oop_node_outs(n)) { bt = T_OBJECT; (*unsafe) = true; } @@ -3530,16 +3527,22 @@ bool ConnectionGraph::is_oop_field(Node* n, int offset, bool* unsafe) { } } else if (adr_type->isa_rawptr() || adr_type->isa_klassptr()) { // Allocation initialization, ThreadLocal field access, unsafe access - if (n->has_out_with(Op_StoreP, Op_LoadP, Op_StoreN, Op_LoadN) || - n->has_out_with(Op_GetAndSetP, Op_GetAndSetN, Op_CompareAndExchangeP, Op_CompareAndExchangeN) || - n->has_out_with(Op_CompareAndSwapP, Op_CompareAndSwapN, Op_WeakCompareAndSwapP, Op_WeakCompareAndSwapN) || - BarrierSet::barrier_set()->barrier_set_c2()->escape_has_out_with_unsafe_object(n)) { + if (has_oop_node_outs(n)) { bt = T_OBJECT; } } } // Note: T_NARROWOOP is not classed as a real reference type - return (is_reference_type(bt) || bt == T_NARROWOOP); + bool res = (is_reference_type(bt) || bt == T_NARROWOOP); + assert(!has_oop_node_outs(n) || res, "sanity: AddP has oop outs, needs to be treated as oop field"); + return res; +} + +bool ConnectionGraph::has_oop_node_outs(Node* n) { + return n->has_out_with(Op_StoreP, Op_LoadP, Op_StoreN, Op_LoadN) || + n->has_out_with(Op_GetAndSetP, Op_GetAndSetN, Op_CompareAndExchangeP, Op_CompareAndExchangeN) || + n->has_out_with(Op_CompareAndSwapP, Op_CompareAndSwapN, Op_WeakCompareAndSwapP, Op_WeakCompareAndSwapN) || + BarrierSet::barrier_set()->barrier_set_c2()->escape_has_out_with_unsafe_object(n); } // Returns unique pointed java object or null. diff --git a/src/hotspot/share/opto/escape.hpp b/src/hotspot/share/opto/escape.hpp index 04a9dc82982..dcb832889ec 100644 --- a/src/hotspot/share/opto/escape.hpp +++ b/src/hotspot/share/opto/escape.hpp @@ -539,7 +539,8 @@ private: } // Helper functions - bool is_oop_field(Node* n, int offset, bool* unsafe); + bool is_oop_field(Node* n, int offset, bool* unsafe); + bool has_oop_node_outs(Node* n); static Node* find_second_addp(Node* addp, Node* n); // offset of a field reference int address_offset(Node* adr, PhaseValues* phase); diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 8dc34af9c19..d68505836d4 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -590,7 +590,7 @@ Node* PhaseIdealLoop::loop_nest_replace_iv(Node* iv_to_replace, Node* inner_iv, // Add a Parse Predicate with an uncommon trap on the failing/false path. Normal control will continue on the true path. void PhaseIdealLoop::add_parse_predicate(Deoptimization::DeoptReason reason, Node* inner_head, IdealLoopTree* loop, SafePointNode* sfpt) { - if (!C->too_many_traps(reason)) { + if (!C->too_many_traps(sfpt->jvms()->method(), sfpt->jvms()->bci(), reason)) { ParsePredicateNode* parse_predicate = new ParsePredicateNode(inner_head->in(LoopNode::EntryControl), reason, &_igvn); register_control(parse_predicate, loop, inner_head->in(LoopNode::EntryControl)); Node* if_false = new IfFalseNode(parse_predicate); @@ -760,6 +760,24 @@ SafePointNode* PhaseIdealLoop::find_safepoint(Node* back_control, Node* x, Ideal return safepoint; } +void PhaseIdealLoop::add_parse_predicates(IdealLoopTree* outer_ilt, LoopNode* inner_head, SafePointNode* cloned_sfpt) { + if (ShortRunningLongLoop) { + add_parse_predicate(Deoptimization::Reason_short_running_long_loop, inner_head, outer_ilt, cloned_sfpt); + } + if (UseLoopPredicate) { + add_parse_predicate(Deoptimization::Reason_predicate, inner_head, outer_ilt, cloned_sfpt); + if (UseProfiledLoopPredicate) { + add_parse_predicate(Deoptimization::Reason_profile_predicate, inner_head, outer_ilt, cloned_sfpt); + } + } + + if (UseAutoVectorizationPredicate) { + add_parse_predicate(Deoptimization::Reason_auto_vectorization_check, inner_head, outer_ilt, cloned_sfpt); + } + + add_parse_predicate(Deoptimization::Reason_loop_limit_check, inner_head, outer_ilt, cloned_sfpt); +} + // If the loop has the shape of a counted loop but with a long // induction variable, transform the loop in a loop nest: an inner // loop that iterates for at most max int iterations with an integer @@ -1123,26 +1141,7 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { if (safepoint != nullptr) { SafePointNode* cloned_sfpt = old_new[safepoint->_idx]->as_SafePoint(); - if (ShortRunningLongLoop) { - add_parse_predicate(Deoptimization::Reason_short_running_long_loop, inner_head, outer_ilt, cloned_sfpt); - } - if (UseLoopPredicate) { - add_parse_predicate(Deoptimization::Reason_predicate, inner_head, outer_ilt, cloned_sfpt); - if (UseProfiledLoopPredicate) { - add_parse_predicate(Deoptimization::Reason_profile_predicate, inner_head, outer_ilt, cloned_sfpt); - } - } - - if (UseAutoVectorizationPredicate) { - // We only want to use the auto-vectorization check as a trap once per bci. And - // PhaseIdealLoop::add_parse_predicate only checks trap limits per method, so - // we do a custom check here. - if (!C->too_many_traps(cloned_sfpt->jvms()->method(), cloned_sfpt->jvms()->bci(), Deoptimization::Reason_auto_vectorization_check)) { - add_parse_predicate(Deoptimization::Reason_auto_vectorization_check, inner_head, outer_ilt, cloned_sfpt); - } - } - - add_parse_predicate(Deoptimization::Reason_loop_limit_check, inner_head, outer_ilt, cloned_sfpt); + add_parse_predicates(outer_ilt, inner_head, cloned_sfpt); } #ifndef PRODUCT @@ -1893,7 +1892,7 @@ bool PhaseIdealLoop::convert_to_long_loop(Node* cmp, Node* phi, IdealLoopTree* l #endif //------------------------------is_counted_loop-------------------------------- -bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_bt) { +bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv_bt) { PhaseGVN *gvn = &_igvn; Node* back_control = loop_exit_control(x, loop); @@ -3528,7 +3527,6 @@ void OuterStripMinedLoopNode::transform_to_counted_loop(PhaseIterGVN* igvn, Phas CountedLoopEndNode* cle = inner_cl->loopexit(); Node* inner_test = cle->in(1); IfNode* outer_le = outer_loop_end(); - CountedLoopEndNode* inner_cle = inner_cl->loopexit(); Node* safepoint = outer_safepoint(); fix_sunk_stores_when_back_to_counted_loop(igvn, iloop); @@ -3754,6 +3752,20 @@ void CountedLoopEndNode::dump_spec(outputStream *st) const { } #endif +IdealLoopTree::IdealLoopTree(PhaseIdealLoop* phase, Node* head, Node* tail): _parent(nullptr), _next(nullptr), _child(nullptr), + _head(head), _tail(tail), + _phase(phase), + _local_loop_unroll_limit(0), _local_loop_unroll_factor(0), + _body(phase->arena()), + _nest(0), _irreducible(0), _has_call(0), _has_sfpt(0), _rce_candidate(0), + _has_range_checks(0), _has_range_checks_computed(0), + _safepts(nullptr), + _required_safept(nullptr), + _allow_optimizations(true) { + precond(_head != nullptr); + precond(_tail != nullptr); +} + //============================================================================= //------------------------------is_member-------------------------------------- // Is 'l' a member of 'this'? @@ -5091,8 +5103,8 @@ void PhaseIdealLoop::build_and_optimize() { // Since nodes do not have a slot for immediate dominator, make // a persistent side array for that info indexed on node->_idx. _idom_size = C->unique(); - _idom = NEW_RESOURCE_ARRAY( Node*, _idom_size ); - _dom_depth = NEW_RESOURCE_ARRAY( uint, _idom_size ); + _idom = NEW_ARENA_ARRAY(&_arena, Node*, _idom_size); + _dom_depth = NEW_ARENA_ARRAY(&_arena, uint, _idom_size); _dom_stk = nullptr; // Allocated on demand in recompute_dom_depth memset( _dom_depth, 0, _idom_size * sizeof(uint) ); @@ -5693,8 +5705,8 @@ void PhaseIdealLoop::set_idom(Node* d, Node* n, uint dom_depth) { uint idx = d->_idx; if (idx >= _idom_size) { uint newsize = next_power_of_2(idx); - _idom = REALLOC_RESOURCE_ARRAY( Node*, _idom,_idom_size,newsize); - _dom_depth = REALLOC_RESOURCE_ARRAY( uint, _dom_depth,_idom_size,newsize); + _idom = REALLOC_ARENA_ARRAY(&_arena, Node*, _idom,_idom_size,newsize); + _dom_depth = REALLOC_ARENA_ARRAY(&_arena, uint, _dom_depth,_idom_size,newsize); memset( _dom_depth + _idom_size, 0, (newsize - _idom_size) * sizeof(uint) ); _idom_size = newsize; } diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index ffc283ac941..5b06f0555ab 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -669,21 +669,7 @@ public: Node_List* _required_safept; // A inner loop cannot delete these safepts; bool _allow_optimizations; // Allow loop optimizations - IdealLoopTree( PhaseIdealLoop* phase, Node *head, Node *tail ) - : _parent(nullptr), _next(nullptr), _child(nullptr), - _head(head), _tail(tail), - _phase(phase), - _local_loop_unroll_limit(0), _local_loop_unroll_factor(0), - _body(Compile::current()->comp_arena()), - _nest(0), _irreducible(0), _has_call(0), _has_sfpt(0), _rce_candidate(0), - _has_range_checks(0), _has_range_checks_computed(0), - _safepts(nullptr), - _required_safept(nullptr), - _allow_optimizations(true) - { - precond(_head != nullptr); - precond(_tail != nullptr); - } + IdealLoopTree(PhaseIdealLoop* phase, Node* head, Node* tail); // Is 'l' a member of 'this'? bool is_member(const IdealLoopTree *l) const; // Test for nested membership @@ -889,6 +875,8 @@ class PhaseIdealLoop : public PhaseTransform { friend class ShenandoahBarrierC2Support; friend class AutoNodeBudget; + Arena _arena; // For data whose lifetime is a single pass of loop optimizations + // Map loop membership for CFG nodes, and ctrl for non-CFG nodes. // // Exception: dead CFG nodes may instead have a ctrl/idom forwarding @@ -1049,6 +1037,8 @@ public: PhaseIterGVN &igvn() const { return _igvn; } + Arena* arena() { return &_arena; }; + bool has_node(const Node* n) const { guarantee(n != nullptr, "No Node."); return _loop_or_ctrl[n->_idx] != nullptr; @@ -1223,7 +1213,8 @@ private: // Compute the Ideal Node to Loop mapping PhaseIdealLoop(PhaseIterGVN& igvn, LoopOptsMode mode) : PhaseTransform(Ideal_Loop), - _loop_or_ctrl(igvn.C->comp_arena()), + _arena(mtCompiler, Arena::Tag::tag_idealloop), + _loop_or_ctrl(&_arena), _igvn(igvn), _verify_me(nullptr), _verify_only(false), @@ -1238,7 +1229,8 @@ private: // or only verify that the graph is valid if verify_me is null. PhaseIdealLoop(PhaseIterGVN& igvn, const PhaseIdealLoop* verify_me = nullptr) : PhaseTransform(Ideal_Loop), - _loop_or_ctrl(igvn.C->comp_arena()), + _arena(mtCompiler, Arena::Tag::tag_idealloop), + _loop_or_ctrl(&_arena), _igvn(igvn), _verify_me(verify_me), _verify_only(verify_me == nullptr), @@ -1368,6 +1360,9 @@ public: #endif void add_parse_predicate(Deoptimization::DeoptReason reason, Node* inner_head, IdealLoopTree* loop, SafePointNode* sfpt); SafePointNode* find_safepoint(Node* back_control, Node* x, IdealLoopTree* loop); + + void add_parse_predicates(IdealLoopTree* outer_ilt, LoopNode* inner_head, SafePointNode* cloned_sfpt); + IdealLoopTree* insert_outer_loop(IdealLoopTree* loop, LoopNode* outer_l, Node* outer_ift); IdealLoopTree* create_outer_strip_mined_loop(Node* init_control, IdealLoopTree* loop, float cl_prob, float le_fcnt, diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 56262d226fc..9470001b2d2 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -2322,12 +2322,7 @@ void PhaseMacroExpand::expand_unlock_node(UnlockNode *unlock) { // No need for a null check on unlock // Make the merge point - Node *region; - Node *mem_phi; - - region = new RegionNode(3); - // create a Phi for the memory state - mem_phi = new PhiNode( region, Type::MEMORY, TypeRawPtr::BOTTOM); + Node* region = new RegionNode(3); FastUnlockNode *funlock = new FastUnlockNode( ctrl, obj, box ); funlock = transform_later( funlock )->as_FastUnlock(); @@ -2356,12 +2351,15 @@ void PhaseMacroExpand::expand_unlock_node(UnlockNode *unlock) { transform_later(region); _igvn.replace_node(_callprojs.fallthrough_proj, region); - Node *memproj = transform_later(new ProjNode(call, TypeFunc::Memory) ); - mem_phi->init_req(1, memproj ); - mem_phi->init_req(2, mem); - transform_later(mem_phi); - - _igvn.replace_node(_callprojs.fallthrough_memproj, mem_phi); + if (_callprojs.fallthrough_memproj != nullptr) { + // create a Phi for the memory state + Node* mem_phi = new PhiNode( region, Type::MEMORY, TypeRawPtr::BOTTOM); + Node* memproj = transform_later(new ProjNode(call, TypeFunc::Memory)); + mem_phi->init_req(1, memproj); + mem_phi->init_req(2, mem); + transform_later(mem_phi); + _igvn.replace_node(_callprojs.fallthrough_memproj, mem_phi); + } } void PhaseMacroExpand::expand_subtypecheck_node(SubTypeCheckNode *check) { diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 52badca8050..ce24c46590d 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -532,6 +532,10 @@ void PhaseValues::init_con_caches() { memset(_zcons,0,sizeof(_zcons)); } +PhaseIterGVN* PhaseValues::is_IterGVN() { + return (_phase == PhaseValuesType::iter_gvn || _phase == PhaseValuesType::ccp) ? static_cast(this) : nullptr; +} + //--------------------------------find_int_type-------------------------------- const TypeInt* PhaseValues::find_int_type(Node* n) { if (n == nullptr) return nullptr; @@ -812,7 +816,7 @@ void PhaseGVN::dump_infinite_loop_info(Node* n, const char* where) { PhaseIterGVN::PhaseIterGVN(PhaseIterGVN* igvn) : _delay_transform(igvn->_delay_transform), _worklist(*C->igvn_worklist()) { - _iterGVN = true; + _phase = PhaseValuesType::iter_gvn; assert(&_worklist == &igvn->_worklist, "sanity"); } @@ -821,7 +825,7 @@ PhaseIterGVN::PhaseIterGVN(PhaseIterGVN* igvn) : _delay_transform(igvn->_delay_t PhaseIterGVN::PhaseIterGVN() : _delay_transform(false), _worklist(*C->igvn_worklist()) { - _iterGVN = true; + _phase = PhaseValuesType::iter_gvn; uint max; // Dead nodes in the hash table inherited from GVN were not treated as @@ -1090,16 +1094,28 @@ void PhaseIterGVN::verify_optimize() { is_verify_invariants()) { ResourceMark rm; Unique_Node_List worklist; - bool failure = false; // BFS all nodes, starting at root worklist.push(C->root()); for (uint j = 0; j < worklist.size(); ++j) { Node* n = worklist.at(j); - if (is_verify_Value()) { failure |= verify_Value_for(n); } - if (is_verify_Ideal()) { failure |= verify_Ideal_for(n, false); } - if (is_verify_Ideal()) { failure |= verify_Ideal_for(n, true); } - if (is_verify_Identity()) { failure |= verify_Identity_for(n); } - if (is_verify_invariants()) { failure |= verify_node_invariants_for(n); } + // If we get an assert here, check why the reported node was not processed again in IGVN. + // We should either make sure that this node is properly added back to the IGVN worklist + // in PhaseIterGVN::add_users_to_worklist to update it again or add an exception + // in the verification methods below if that is not possible for some reason (like Load nodes). + if (is_verify_Value()) { + verify_Value_for(n); + } + if (is_verify_Ideal()) { + verify_Ideal_for(n, false); + verify_Ideal_for(n, true); + } + if (is_verify_Identity()) { + verify_Identity_for(n); + } + if (is_verify_invariants()) { + verify_node_invariants_for(n); + } + // traverse all inputs and outputs for (uint i = 0; i < n->req(); i++) { if (n->in(i) != nullptr) { @@ -1110,11 +1126,6 @@ void PhaseIterGVN::verify_optimize() { worklist.push(n->fast_out(i)); } } - // If we get this assert, check why the reported nodes were not processed again in IGVN. - // We should either make sure that these nodes are properly added back to the IGVN worklist - // in PhaseIterGVN::add_users_to_worklist to update them again or add an exception - // in the verification code above if that is not possible for some reason (like Load nodes). - assert(!failure, "Missed optimization opportunity/broken graph in PhaseIterGVN"); } verify_empty_worklist(nullptr); @@ -1139,18 +1150,18 @@ void PhaseIterGVN::verify_empty_worklist(Node* node) { assert(false, "igvn worklist must still be empty after verify"); } -// Check that type(n) == n->Value(), return true if we have a failure. +// Check that type(n) == n->Value(), asserts if we have a failure. // We have a list of exceptions, see detailed comments in code. // (1) Integer "widen" changes, but the range is the same. // (2) LoadNode performs deep traversals. Load is not notified for changes far away. // (3) CmpPNode performs deep traversals if it compares oopptr. CmpP is not notified for changes far away. -bool PhaseIterGVN::verify_Value_for(Node* n, bool strict) { +void PhaseIterGVN::verify_Value_for(const Node* n, bool strict) { // If we assert inside type(n), because the type is still a null, then maybe // the node never went through gvn.transform, which would be a bug. const Type* told = type(n); const Type* tnew = n->Value(this); if (told == tnew) { - return false; + return; } // Exception (1) // Integer "widen" changes, but range is the same. @@ -1159,7 +1170,7 @@ bool PhaseIterGVN::verify_Value_for(Node* n, bool strict) { const TypeInteger* t1 = tnew->is_integer(tnew->basic_type()); if (t0->lo_as_long() == t1->lo_as_long() && t0->hi_as_long() == t1->hi_as_long()) { - return false; // ignore integer widen + return; // ignore integer widen } } // Exception (2) @@ -1168,7 +1179,7 @@ bool PhaseIterGVN::verify_Value_for(Node* n, bool strict) { // MemNode::can_see_stored_value looks up through many memory nodes, // which means we would need to notify modifications from far up in // the inputs all the way down to the LoadNode. We don't do that. - return false; + return; } // Exception (3) // CmpPNode performs deep traversals if it compares oopptr. CmpP is not notified for changes far away. @@ -1184,10 +1195,10 @@ bool PhaseIterGVN::verify_Value_for(Node* n, bool strict) { // control sub of the allocation. The problems is that sometimes dominates answers // false conservatively, and later it can determine that it is indeed true. Loops with // Region heads can lead to giving up, whereas LoopNodes can be skipped easier, and - // so the traversal becomes more powerful. This is difficult to remidy, we would have + // so the traversal becomes more powerful. This is difficult to remedy, we would have // to notify the CmpP of CFG updates. Luckily, we recompute CmpP::Value during CCP // after loop-opts, so that should take care of many of these cases. - return false; + return; } stringStream ss; // Print as a block without tty lock. @@ -1201,13 +1212,24 @@ bool PhaseIterGVN::verify_Value_for(Node* n, bool strict) { tnew->dump_on(&ss); ss.cr(); tty->print_cr("%s", ss.as_string()); - return true; + + switch (_phase) { + case PhaseValuesType::iter_gvn: + assert(false, "Missed Value optimization opportunity in PhaseIterGVN for %s",n->Name()); + break; + case PhaseValuesType::ccp: + assert(false, "PhaseCCP not at fixpoint: analysis result may be unsound for %s", n->Name()); + break; + default: + assert(false, "Unexpected phase"); + break; + } } // Check that all Ideal optimizations that could be done were done. -// Returns true if it found missed optimization opportunities and -// false otherwise (no missed optimization, or skipped verification). -bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { +// Asserts if it found missed optimization opportunities or encountered unexpected changes, and +// returns normally otherwise (no missed optimization, or skipped verification). +void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // First, we check a list of exceptions, where we skip verification, // because there are known cases where Ideal can optimize after IGVN. // Some may be expected and cannot be fixed, and others should be fixed. @@ -1221,7 +1243,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xbatch --version case Op_RangeCheck: - return false; + return; // IfNode::Ideal does: // Node* prev_dom = search_identical(dist, igvn); @@ -1232,7 +1254,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_If: - return false; + return; // IfNode::simple_subsuming // Looks for dominating test that subsumes the current test. @@ -1242,7 +1264,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // runtime/exceptionMsgs/ArrayIndexOutOfBoundsException/ArrayIndexOutOfBoundsExceptionTest.java#id1 // -XX:VerifyIterativeGVN=1110 case Op_CountedLoopEnd: - return false; + return; // LongCountedLoopEndNode::Ideal // Probably same issue as above. @@ -1251,7 +1273,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // compiler/predicates/assertion/TestAssertionPredicates.java#NoLoopPredicationXbatch // -XX:StressLongCountedLoop=2000000 -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 case Op_LongCountedLoopEnd: - return false; + return; // RegionNode::Ideal does "Skip around the useless IF diamond". // 245 IfTrue === 244 @@ -1270,7 +1292,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_Region: - return false; + return; // In AddNode::Ideal, we call "commute", which swaps the inputs so // that smaller idx are first. Tracking it back, it led me to @@ -1349,7 +1371,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { case Op_MulHF: case Op_MaxHF: case Op_MinHF: - return false; + return; // In MulNode::Ideal the edges can be swapped to help value numbering: // @@ -1373,7 +1395,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java // -XX:VerifyIterativeGVN=1110 case Op_AndL: - return false; + return; // SubLNode::Ideal does transform like: // Convert "c1 - (y+c0)" into "(c1-c0) - y" @@ -1405,7 +1427,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_SubL: - return false; + return; // SubINode::Ideal does // Convert "x - (y+c0)" into "(x-y) - c0" AND @@ -1417,7 +1439,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // test/hotspot/jtreg/compiler/c2/IVTest.java // -XX:VerifyIterativeGVN=1110 case Op_SubI: - return false; + return; // AddNode::IdealIL does transform like: // Convert x + (con - y) into "(x - y) + con" @@ -1446,7 +1468,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_AddL: - return false; + return; // SubTypeCheckNode::Ideal calls SubTypeCheckNode::verify_helper, which does // Node* cmp = phase->transform(new CmpPNode(subklass, in(SuperKlass))); @@ -1471,7 +1493,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xbatch --version case Op_SubTypeCheck: - return false; + return; // LoopLimitNode::Ideal when stride is constant power-of-2, we can do a lowering // to other nodes: Conv, Add, Sub, Mul, And ... @@ -1491,7 +1513,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Fond with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_LoopLimit: - return false; + return; // PhiNode::Ideal calls split_flow_path, which tries to do this: // "This optimization tries to find two or more inputs of phi with the same constant @@ -1514,7 +1536,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_Phi: - return false; + return; // MemBarNode::Ideal does "Eliminate volatile MemBars for scalar replaced objects". // For examle "The allocated object does not escape". @@ -1527,7 +1549,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_MemBarStoreStore: - return false; + return; // ConvI2LNode::Ideal converts // 648 AddI === _ 583 645 [[ 661 ]] @@ -1543,7 +1565,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_ConvI2L: - return false; + return; // AddNode::IdealIL can do this transform (and similar other ones): // Convert "a*b+a*c into a*(b+c) @@ -1557,7 +1579,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // test/hotspot/jtreg/compiler/loopopts/superword/ReductionPerf.java // -XX:VerifyIterativeGVN=1110 case Op_AddI: - return false; + return; // ArrayCopyNode::Ideal // calls ArrayCopyNode::prepare_array_copy @@ -1583,7 +1605,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // compiler/arraycopy/TestArrayCopyAsLoadsStores.java // -XX:VerifyIterativeGVN=1110 case Op_ArrayCopy: - return false; + return; // CastLLNode::Ideal // calls ConstraintCastNode::optimize_integer_cast -> pushes CastLL through SubL @@ -1595,7 +1617,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // compiler/c2/TestMergeStoresMemorySegment.java#byte-array // -XX:VerifyIterativeGVN=1110 case Op_CastLL: - return false; + return; // Similar case happens to CastII // @@ -1603,7 +1625,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // compiler/c2/TestScalarReplacementMaxLiveNodes.java // -XX:VerifyIterativeGVN=1110 case Op_CastII: - return false; + return; // MaxLNode::Ideal // calls AddNode::Ideal @@ -1618,7 +1640,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // -XX:VerifyIterativeGVN=1110 case Op_MaxL: case Op_MinL: - return false; + return; // OrINode::Ideal // calls AddNode::Ideal @@ -1632,7 +1654,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // -XX:VerifyIterativeGVN=1110 case Op_OrI: case Op_OrL: - return false; + return; // Bool -> constant folded to 1. // Issue with notification? @@ -1641,7 +1663,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // compiler/c2/irTests/TestVectorizationMismatchedAccess.java // -XX:VerifyIterativeGVN=1110 case Op_Bool: - return false; + return; // LShiftLNode::Ideal // Looks at pattern: "(x + x) << c0", converts it to "x << (c0 + 1)" @@ -1651,7 +1673,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // compiler/conversions/TestMoveConvI2LOrCastIIThruAddIs.java // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 case Op_LShiftL: - return false; + return; // LShiftINode::Ideal // pattern: ((x + con1) << con2) -> x << con2 + con1 << con2 @@ -1664,18 +1686,18 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // compiler/escapeAnalysis/Test6689060.java // -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 case Op_LShiftI: - return false; + return; // AddPNode::Ideal seems to do set_req without removing lock first. // Found with various vector tests tier1-tier3. case Op_AddP: - return false; + return; // StrIndexOfNode::Ideal // Found in tier1-3. case Op_StrIndexOf: case Op_StrIndexOfChar: - return false; + return; // StrEqualsNode::Identity // @@ -1684,7 +1706,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // -XX:+UnlockExperimentalVMOptions -XX:LockingMode=1 -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 // Note: The -XX:LockingMode option is not available anymore. case Op_StrEquals: - return false; + return; // AryEqNode::Ideal // Not investigated. Reshapes itself and adds lots of nodes to the worklist. @@ -1693,22 +1715,22 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // vmTestbase/vm/mlvm/meth/stress/compiler/i2c_c2i/Test.java // -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation -XX:+StressUnstableIfTraps -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 case Op_AryEq: - return false; + return; // MergeMemNode::Ideal // Found in tier1-3. Did not investigate further yet. case Op_MergeMem: - return false; + return; // URShiftINode::Ideal // Found in tier1-3. Did not investigate further yet. case Op_URShiftI: - return false; + return; // CMoveINode::Ideal // Found in tier1-3. Did not investigate further yet. case Op_CMoveI: - return false; + return; // CmpPNode::Ideal calls isa_const_java_mirror // and generates new constant nodes, even if no progress is made. @@ -1719,14 +1741,14 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=1110 -Xcomp --version case Op_CmpP: - return false; + return; // MinINode::Ideal // Did not investigate, but there are some patterns that might // need more notification. case Op_MinI: case Op_MaxI: // preemptively removed it as well. - return false; + return; } if (n->is_Load()) { @@ -1742,7 +1764,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // test/hotspot/jtreg/compiler/arraycopy/TestCloneAccess.java // -XX:VerifyIterativeGVN=1110 - return false; + return; } if (n->is_Store()) { @@ -1756,7 +1778,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version - return false; + return; } if (n->is_Vector()) { @@ -1775,7 +1797,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // compiler/vectorapi/TestMaskedMacroLogicVector.java // -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 -XX:+UseParallelGC -XX:+UseNUMA - return false; + return; } if (n->is_Region()) { @@ -1794,7 +1816,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // compiler/eliminateAutobox/TestShortBoxing.java // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 - return false; + return; } if (n->is_CallJava()) { @@ -1826,13 +1848,19 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-U // -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 - return false; + return; } // The number of nodes shoud not increase. uint old_unique = C->unique(); // The hash of a node should not change, this would indicate different inputs uint old_hash = n->hash(); + // Remove 'n' from hash table in case it gets modified. We want to avoid + // hitting the "Need to remove from hash before changing edges" assert if + // a change occurs. Instead, we would like to proceed with the optimization, + // return and finally hit the assert in PhaseIterGVN::verify_optimize to get + // a more meaningful message + _table.hash_delete(n); Node* i = n->Ideal(this, can_reshape); // If there was no new Idealization, we are probably happy. if (i == nullptr) { @@ -1843,7 +1871,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { ss.print_cr(" old_unique = %d, unique = %d", old_unique, C->unique()); n->dump_bfs(1, nullptr, "", &ss); tty->print_cr("%s", ss.as_string()); - return true; + assert(false, "Unexpected new unused nodes from applying Ideal optimization on %s", n->Name()); } if (old_hash != n->hash()) { @@ -1853,13 +1881,14 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { ss.print_cr(" old_hash = %d, hash = %d", old_hash, n->hash()); n->dump_bfs(1, nullptr, "", &ss); tty->print_cr("%s", ss.as_string()); - return true; + assert(false, "Unexpected hash change from applying Ideal optimization on %s", n->Name()); } verify_empty_worklist(n); // Everything is good. - return false; + hash_find_insert(n); + return; } // We just saw a new Idealization which was not done during IGVN. @@ -1876,13 +1905,14 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { ss.print_cr("The result after Ideal:"); i->dump_bfs(1, nullptr, "", &ss); tty->print_cr("%s", ss.as_string()); - return true; + + assert(false, "Missed Ideal optimization opportunity in PhaseIterGVN for %s", n->Name()); } // Check that all Identity optimizations that could be done were done. -// Returns true if it found missed optimization opportunities and -// false otherwise (no missed optimization, or skipped verification). -bool PhaseIterGVN::verify_Identity_for(Node* n) { +// Asserts if it found missed optimization opportunities, and +// returns normally otherwise (no missed optimization, or skipped verification). +void PhaseIterGVN::verify_Identity_for(Node* n) { // First, we check a list of exceptions, where we skip verification, // because there are known cases where Ideal can optimize after IGVN. // Some may be expected and cannot be fixed, and others should be fixed. @@ -1901,7 +1931,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // Found with: // java -XX:VerifyIterativeGVN=1000 -Xcomp --version case Op_SafePoint: - return false; + return; // MergeMemNode::Identity replaces the MergeMem with its base_memory if it // does not record any other memory splits. @@ -1912,7 +1942,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // Found with: // java -XX:VerifyIterativeGVN=1000 -Xcomp --version case Op_MergeMem: - return false; + return; // ConstraintCastNode::Identity finds casts that are the same, except that // the control is "higher up", i.e. dominates. The call goes via @@ -1926,7 +1956,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { case Op_CastPP: case Op_CastII: case Op_CastLL: - return false; + return; // Same issue for CheckCastPP, uses ConstraintCastNode::Identity and // checks dominator, which may be changed, but too far up for notification @@ -1936,7 +1966,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // compiler/c2/irTests/TestSkeletonPredicates.java // -XX:VerifyIterativeGVN=1110 case Op_CheckCastPP: - return false; + return; // In SubNode::Identity, we do: // Convert "(X+Y) - Y" into X and "(X+Y) - X" into Y @@ -1954,7 +1984,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // java -XX:VerifyIterativeGVN=1000 -Xcomp --version case Op_SubI: case Op_SubL: - return false; + return; // PhiNode::Identity checks for patterns like: // r = (x != con) ? x : con; @@ -1968,7 +1998,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java // -XX:VerifyIterativeGVN=1110 case Op_Phi: - return false; + return; // ConvI2LNode::Identity does // convert I2L(L2I(x)) => x @@ -1979,7 +2009,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-A // -XX:VerifyIterativeGVN=1110 case Op_ConvI2L: - return false; + return; // MaxNode::find_identity_operation // Finds patterns like Max(A, Max(A, B)) -> Max(A, B) @@ -1999,7 +2029,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { case Op_MinHF: case Op_MaxD: case Op_MinD: - return false; + return; // AddINode::Identity @@ -2013,12 +2043,12 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 case Op_AddI: case Op_AddL: - return false; + return; // AbsINode::Identity // Not investigated yet. case Op_AbsI: - return false; + return; } if (n->is_Load()) { @@ -2032,7 +2062,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // // Found with: // java -XX:VerifyIterativeGVN=1000 -Xcomp --version - return false; + return; } if (n->is_Store()) { @@ -2043,20 +2073,20 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // Found with: // applications/ctw/modules/java_base_2.java // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -Djava.awt.headless=true -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 - return false; + return; } if (n->is_Vector()) { // Found with tier1-3. Not investigated yet. // The observed issue was with AndVNode::Identity - return false; + return; } Node* i = n->Identity(this); // If we cannot find any other Identity, we are happy. if (i == n) { verify_empty_worklist(n); - return false; + return; } // The verification just found a new Identity that was not found during IGVN. @@ -2068,11 +2098,12 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { ss.print_cr("New node:"); i->dump_bfs(1, nullptr, "", &ss); tty->print_cr("%s", ss.as_string()); - return true; + + assert(false, "Missed Identity optimization opportunity in PhaseIterGVN for %s", n->Name()); } // Some other verifications that are not specific to a particular transformation. -bool PhaseIterGVN::verify_node_invariants_for(const Node* n) { +void PhaseIterGVN::verify_node_invariants_for(const Node* n) { if (n->is_AddP()) { if (!n->as_AddP()->address_input_has_same_base()) { stringStream ss; // Print as a block without tty lock. @@ -2080,10 +2111,10 @@ bool PhaseIterGVN::verify_node_invariants_for(const Node* n) { ss.print_cr("Base pointers must match for AddP chain:"); n->dump_bfs(2, nullptr, "", &ss); tty->print_cr("%s", ss.as_string()); - return true; + + assert(false, "Broken node invariant for %s", n->Name()); } } - return false; } #endif @@ -2794,6 +2825,7 @@ uint PhaseCCP::_total_constants = 0; PhaseCCP::PhaseCCP( PhaseIterGVN *igvn ) : PhaseIterGVN(igvn) { NOT_PRODUCT( clear_constants(); ) assert( _worklist.size() == 0, "" ); + _phase = PhaseValuesType::ccp; analyze(); } @@ -2914,17 +2946,18 @@ bool PhaseCCP::needs_revisit(Node* n) const { // Note for CCP the non-convergence can lead to unsound analysis and mis-compilation. // Therefore, we are verifying Value convergence strictly. void PhaseCCP::verify_analyze(Unique_Node_List& worklist_verify) { - bool failure = false; while (worklist_verify.size()) { Node* n = worklist_verify.pop(); - failure |= verify_Value_for(n, /* strict = */ true); + + // An assert in verify_Value_for means that PhaseCCP is not at fixpoint + // and that the analysis result may be unsound. + // If this happens, check why the reported nodes were not processed again in CCP. + // We should either make sure that these nodes are properly added back to the CCP worklist + // in PhaseCCP::push_child_nodes_to_worklist() to update their type in the same round, + // or that they are added in PhaseCCP::needs_revisit() so that analysis revisits + // them at the end of the round. + verify_Value_for(n, true); } - // If we get this assert, check why the reported nodes were not processed again in CCP. - // We should either make sure that these nodes are properly added back to the CCP worklist - // in PhaseCCP::push_child_nodes_to_worklist() to update their type in the same round, - // or that they are added in PhaseCCP::needs_revisit() so that analysis revisits - // them at the end of the round. - assert(!failure, "PhaseCCP not at fixpoint: analysis result may be unsound."); } #endif diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index 3f75aab8980..ce02f456c00 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -224,7 +224,13 @@ public: // 3) NodeHash table, to find identical nodes (and remove/update the hash of a node on modification). class PhaseValues : public PhaseTransform { protected: - bool _iterGVN; + enum class PhaseValuesType { + gvn, + iter_gvn, + ccp + }; + + PhaseValuesType _phase; // Hash table for value-numbering. Reference to "C->node_hash()", NodeHash &_table; @@ -247,7 +253,7 @@ protected: void init_con_caches(); public: - PhaseValues() : PhaseTransform(GVN), _iterGVN(false), + PhaseValues() : PhaseTransform(GVN), _phase(PhaseValuesType::gvn), _table(*C->node_hash()), _types(*C->types()) { NOT_PRODUCT( clear_new_values(); ) @@ -256,7 +262,7 @@ public: init_con_caches(); } NOT_PRODUCT(~PhaseValues();) - PhaseIterGVN* is_IterGVN() { return (_iterGVN) ? (PhaseIterGVN*)this : nullptr; } + PhaseIterGVN* is_IterGVN(); // Some Ideal and other transforms delete --> modify --> insert values bool hash_delete(Node* n) { return _table.hash_delete(n); } @@ -490,10 +496,10 @@ public: void optimize(); #ifdef ASSERT void verify_optimize(); - bool verify_Value_for(Node* n, bool strict = false); - bool verify_Ideal_for(Node* n, bool can_reshape); - bool verify_Identity_for(Node* n); - bool verify_node_invariants_for(const Node* n); + void verify_Value_for(const Node* n, bool strict = false); + void verify_Ideal_for(Node* n, bool can_reshape); + void verify_Identity_for(Node* n); + void verify_node_invariants_for(const Node* n); void verify_empty_worklist(Node* n); #endif diff --git a/src/hotspot/share/opto/subnode.cpp b/src/hotspot/share/opto/subnode.cpp index a10cd2a8d5b..548df58eb35 100644 --- a/src/hotspot/share/opto/subnode.cpp +++ b/src/hotspot/share/opto/subnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -745,71 +745,40 @@ const Type* CmpINode::Value(PhaseGVN* phase) const { // Simplify a CmpU (compare 2 integers) node, based on local information. // If both inputs are constants, compare them. -const Type *CmpUNode::sub( const Type *t1, const Type *t2 ) const { - assert(!t1->isa_ptr(), "obsolete usage of CmpU"); +const Type* CmpUNode::sub(const Type* t1, const Type* t2) const { + const TypeInt* r0 = t1->is_int(); + const TypeInt* r1 = t2->is_int(); - // comparing two unsigned ints - const TypeInt *r0 = t1->is_int(); // Handy access - const TypeInt *r1 = t2->is_int(); - - // Current installed version - // Compare ranges for non-overlap - juint lo0 = r0->_lo; - juint hi0 = r0->_hi; - juint lo1 = r1->_lo; - juint hi1 = r1->_hi; - - // If either one has both negative and positive values, - // it therefore contains both 0 and -1, and since [0..-1] is the - // full unsigned range, the type must act as an unsigned bottom. - bool bot0 = ((jint)(lo0 ^ hi0) < 0); - bool bot1 = ((jint)(lo1 ^ hi1) < 0); - - if (bot0 || bot1) { - // All unsigned values are LE -1 and GE 0. - if (lo0 == 0 && hi0 == 0) { - return TypeInt::CC_LE; // 0 <= bot - } else if ((jint)lo0 == -1 && (jint)hi0 == -1) { - return TypeInt::CC_GE; // -1 >= bot - } else if (lo1 == 0 && hi1 == 0) { - return TypeInt::CC_GE; // bot >= 0 - } else if ((jint)lo1 == -1 && (jint)hi1 == -1) { - return TypeInt::CC_LE; // bot <= -1 - } - } else { - // We can use ranges of the form [lo..hi] if signs are the same. - assert(lo0 <= hi0 && lo1 <= hi1, "unsigned ranges are valid"); - // results are reversed, '-' > '+' for unsigned compare - if (hi0 < lo1) { - return TypeInt::CC_LT; // smaller - } else if (lo0 > hi1) { - return TypeInt::CC_GT; // greater - } else if (hi0 == lo1 && lo0 == hi1) { - return TypeInt::CC_EQ; // Equal results - } else if (lo0 >= hi1) { - return TypeInt::CC_GE; - } else if (hi0 <= lo1) { - // Check for special case in Hashtable::get. (See below.) - if ((jint)lo0 >= 0 && (jint)lo1 >= 0 && is_index_range_check()) - return TypeInt::CC_LT; - return TypeInt::CC_LE; - } - } // Check for special case in Hashtable::get - the hash index is // mod'ed to the table size so the following range check is useless. // Check for: (X Mod Y) CmpU Y, where the mod result and Y both have // to be positive. // (This is a gross hack, since the sub method never // looks at the structure of the node in any other case.) - if ((jint)lo0 >= 0 && (jint)lo1 >= 0 && is_index_range_check()) + if (r0->_lo >= 0 && r1->_lo >= 0 && is_index_range_check()) { return TypeInt::CC_LT; + } + + if (r0->_uhi < r1->_ulo) { + return TypeInt::CC_LT; + } else if (r0->_ulo > r1->_uhi) { + return TypeInt::CC_GT; + } else if (r0->is_con() && r1->is_con()) { + // Since r0->_ulo == r0->_uhi == r0->get_con(), we only reach here if the constants are equal + assert(r0->get_con() == r1->get_con(), "must reach a previous branch otherwise"); + return TypeInt::CC_EQ; + } else if (r0->_uhi == r1->_ulo) { + return TypeInt::CC_LE; + } else if (r0->_ulo == r1->_uhi) { + return TypeInt::CC_GE; + } const Type* joined = r0->join(r1); if (joined == Type::TOP) { return TypeInt::CC_NE; } - return TypeInt::CC; // else use worst case results + return TypeInt::CC; } const Type* CmpUNode::Value(PhaseGVN* phase) const { @@ -963,51 +932,21 @@ const Type *CmpLNode::sub( const Type *t1, const Type *t2 ) const { // Simplify a CmpUL (compare 2 unsigned longs) node, based on local information. // If both inputs are constants, compare them. const Type* CmpULNode::sub(const Type* t1, const Type* t2) const { - assert(!t1->isa_ptr(), "obsolete usage of CmpUL"); - - // comparing two unsigned longs - const TypeLong* r0 = t1->is_long(); // Handy access + const TypeLong* r0 = t1->is_long(); const TypeLong* r1 = t2->is_long(); - // Current installed version - // Compare ranges for non-overlap - julong lo0 = r0->_lo; - julong hi0 = r0->_hi; - julong lo1 = r1->_lo; - julong hi1 = r1->_hi; - - // If either one has both negative and positive values, - // it therefore contains both 0 and -1, and since [0..-1] is the - // full unsigned range, the type must act as an unsigned bottom. - bool bot0 = ((jlong)(lo0 ^ hi0) < 0); - bool bot1 = ((jlong)(lo1 ^ hi1) < 0); - - if (bot0 || bot1) { - // All unsigned values are LE -1 and GE 0. - if (lo0 == 0 && hi0 == 0) { - return TypeInt::CC_LE; // 0 <= bot - } else if ((jlong)lo0 == -1 && (jlong)hi0 == -1) { - return TypeInt::CC_GE; // -1 >= bot - } else if (lo1 == 0 && hi1 == 0) { - return TypeInt::CC_GE; // bot >= 0 - } else if ((jlong)lo1 == -1 && (jlong)hi1 == -1) { - return TypeInt::CC_LE; // bot <= -1 - } - } else { - // We can use ranges of the form [lo..hi] if signs are the same. - assert(lo0 <= hi0 && lo1 <= hi1, "unsigned ranges are valid"); - // results are reversed, '-' > '+' for unsigned compare - if (hi0 < lo1) { - return TypeInt::CC_LT; // smaller - } else if (lo0 > hi1) { - return TypeInt::CC_GT; // greater - } else if (hi0 == lo1 && lo0 == hi1) { - return TypeInt::CC_EQ; // Equal results - } else if (lo0 >= hi1) { - return TypeInt::CC_GE; - } else if (hi0 <= lo1) { - return TypeInt::CC_LE; - } + if (r0->_uhi < r1->_ulo) { + return TypeInt::CC_LT; + } else if (r0->_ulo > r1->_uhi) { + return TypeInt::CC_GT; + } else if (r0->is_con() && r1->is_con()) { + // Since r0->_ulo == r0->_uhi == r0->get_con(), we only reach here if the constants are equal + assert(r0->get_con() == r1->get_con(), "must reach a previous branch otherwise"); + return TypeInt::CC_EQ; + } else if (r0->_uhi == r1->_ulo) { + return TypeInt::CC_LE; + } else if (r0->_ulo == r1->_uhi) { + return TypeInt::CC_GE; } const Type* joined = r0->join(r1); @@ -1015,7 +954,7 @@ const Type* CmpULNode::sub(const Type* t1, const Type* t2) const { return TypeInt::CC_NE; } - return TypeInt::CC; // else use worst case results + return TypeInt::CC; } //============================================================================= diff --git a/src/hotspot/share/opto/vector.cpp b/src/hotspot/share/opto/vector.cpp index cf01b2442e6..f44df7e6da2 100644 --- a/src/hotspot/share/opto/vector.cpp +++ b/src/hotspot/share/opto/vector.cpp @@ -326,37 +326,15 @@ Node* PhaseVector::expand_vbox_node_helper(Node* vbox, return expand_vbox_alloc_node(vbox_alloc, vect, box_type, vect_type); } - // Handle the case when both the allocation input and vector input to - // VectorBoxNode are Phi. This case is generated after the transformation of - // Phi: Phi (VectorBox1 VectorBox2) => VectorBox (Phi1 Phi2). - // With this optimization, the relative two allocation inputs of VectorBox1 and - // VectorBox2 are gathered into Phi1 now. Similarly, the original vector - // inputs of two VectorBox nodes are in Phi2. - // - // See PhiNode::merge_through_phi in cfg.cpp for more details. - if (vbox->is_Phi() && vect->is_Phi()) { - assert(vbox->as_Phi()->region() == vect->as_Phi()->region(), ""); + // Handle the case when the allocation input to VectorBoxNode is a Phi. + // This is generated after the transformation in PhiNode::merge_through_phi: + // Phi (VectorBox1 VectorBox2) => VectorBox (Phi1 Phi2) + // The vector input may also be a Phi (Phi2 above), or it may have been + // value-numbered to a single node if all inputs were identical. + if (vbox->is_Phi()) { + bool same_region = vect->is_Phi() && vbox->as_Phi()->region() == vect->as_Phi()->region(); for (uint i = 1; i < vbox->req(); i++) { - Node* new_box = expand_vbox_node_helper(vbox->in(i), vect->in(i), - box_type, vect_type, visited); - if (!new_box->is_Phi()) { - C->initial_gvn()->hash_delete(vbox); - vbox->set_req(i, new_box); - } - } - return C->initial_gvn()->transform(vbox); - } - - // Handle the case when the allocation input to VectorBoxNode is a phi - // but the vector input is not, which can definitely be the case if the - // vector input has been value-numbered. It seems to be safe to do by - // construction because VectorBoxNode and VectorBoxAllocate come in a - // specific order as a result of expanding an intrinsic call. After that, if - // any of the inputs to VectorBoxNode are value-numbered they can only - // move up and are guaranteed to dominate. - if (vbox->is_Phi() && (vect->is_Vector() || vect->is_LoadVector())) { - for (uint i = 1; i < vbox->req(); i++) { - Node* new_box = expand_vbox_node_helper(vbox->in(i), vect, + Node* new_box = expand_vbox_node_helper(vbox->in(i), same_region ? vect->in(i) : vect, box_type, vect_type, visited); if (!new_box->is_Phi()) { C->initial_gvn()->hash_delete(vbox); diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index 65d54e076b6..883a0526053 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -175,6 +175,10 @@ Node* GraphKit::unbox_vector(Node* v, const TypeInstPtr* vbox_type, BasicType el assert(check_vbox(vbox_type), ""); const TypeVect* vt = TypeVect::make(elem_bt, num_elem, is_vector_mask(vbox_type->instance_klass())); Node* unbox = gvn().transform(new VectorUnboxNode(C, vt, v, merged_memory())); + if (gvn().type(unbox)->isa_vect() == nullptr) { + assert(gvn().type(unbox) == Type::TOP, "sanity"); + return nullptr; // not a vector + } return unbox; } diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index 271dc901dcb..7401efaf7c2 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, 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 @@ -1919,6 +1919,15 @@ Node* VectorMaskToLongNode::Ideal_MaskAll(PhaseGVN* phase) { // saved with a predicate type. if (in1->Opcode() == Op_VectorStoreMask) { Node* mask = in1->in(1); + // Skip the optimization if the mask is dead. + if (phase->type(mask) == Type::TOP) { + return nullptr; + } + // If the ideal graph is transformed correctly, the input mask should be a + // vector type node. Following optimization can ignore the mismatched type + // issue. But we still keep the sanity check for the mask type by using + // "is_vect()" in the assertion below, so that there can be less optimizations + // evolved before the compiler finally runs into a problem. assert(!Matcher::mask_op_prefers_predicate(Opcode(), mask->bottom_type()->is_vect()), "sanity"); in1 = mask; } diff --git a/src/hotspot/share/prims/methodHandles.cpp b/src/hotspot/share/prims/methodHandles.cpp index c243cae20ab..584f077eddc 100644 --- a/src/hotspot/share/prims/methodHandles.cpp +++ b/src/hotspot/share/prims/methodHandles.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, 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 @@ -557,7 +557,7 @@ bool MethodHandles::is_basic_type_signature(Symbol* sig) { switch (ss.type()) { case T_OBJECT: // only java/lang/Object is valid here - if (strncmp((char*) ss.raw_bytes(), OBJ_SIG, OBJ_SIG_LEN) != 0) + if (strncmp((char*) ss.raw_bytes(), OBJ_SIG, ss.raw_length()) != 0) return false; break; case T_VOID: diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index de5bc9ea58f..35e0b83d25f 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -578,7 +578,7 @@ WB_END WB_ENTRY(jboolean, WB_G1InConcurrentMark(JNIEnv* env, jobject o)) if (UseG1GC) { G1CollectedHeap* g1h = G1CollectedHeap::heap(); - return g1h->concurrent_mark()->cm_thread()->in_progress(); + return g1h->concurrent_mark()->in_progress(); } THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1InConcurrentMark: G1 GC is not enabled"); WB_END diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 785ee2af592..144533cd959 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -339,15 +339,6 @@ void ObjectMonitor::ExitOnSuspend::operator()(JavaThread* current) { } } -void ObjectMonitor::ClearSuccOnSuspend::operator()(JavaThread* current) { - if (current->is_suspended()) { - if (_om->has_successor(current)) { - _om->clear_successor(); - OrderAccess::fence(); // always do a full fence when successor is cleared - } - } -} - #define assert_mark_word_consistency() \ assert(UseObjectMonitorTable || object()->mark() == markWord::encode(this), \ "object mark must match encoded this: mark=" INTPTR_FORMAT \ @@ -500,7 +491,7 @@ bool ObjectMonitor::spin_enter(JavaThread* current) { return false; } -bool ObjectMonitor::enter(JavaThread* current) { +bool ObjectMonitor::enter(JavaThread* current, bool post_jvmti_events) { assert(current == JavaThread::current(), "must be"); if (spin_enter(current)) { @@ -521,15 +512,15 @@ bool ObjectMonitor::enter(JavaThread* current) { } // At this point this ObjectMonitor cannot be deflated, finish contended enter - enter_with_contention_mark(current, contention_mark); + enter_with_contention_mark(current, contention_mark, post_jvmti_events); return true; } -void ObjectMonitor::notify_contended_enter(JavaThread* current) { +void ObjectMonitor::notify_contended_enter(JavaThread* current, bool post_jvmti_events) { current->set_current_pending_monitor(this); DTRACE_MONITOR_PROBE(contended__enter, this, object(), current); - if (JvmtiExport::should_post_monitor_contended_enter()) { + if (post_jvmti_events && JvmtiExport::should_post_monitor_contended_enter()) { JvmtiExport::post_monitor_contended_enter(current, this); // The current thread does not yet own the monitor and does not @@ -540,7 +531,7 @@ void ObjectMonitor::notify_contended_enter(JavaThread* current) { } } -void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark &cm) { +void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark &cm, bool post_jvmti_events) { assert(current == JavaThread::current(), "must be"); assert(!has_owner(current), "must be"); assert(cm._monitor == this, "must be"); @@ -564,7 +555,7 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito ContinuationEntry* ce = current->last_continuation(); bool is_virtual = ce != nullptr && ce->is_virtual_thread(); if (is_virtual) { - notify_contended_enter(current); + notify_contended_enter(current, post_jvmti_events); result = Continuation::try_preempt(current, ce->cont_oop(current)); if (result == freeze_ok) { bool acquired = vthread_monitor_enter(current); @@ -573,7 +564,7 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito // _entry_list so cancel preemption. We will still go through the preempt stub // but instead of unmounting we will call thaw to continue execution. current->set_preemption_cancelled(true); - if (JvmtiExport::should_post_monitor_contended_entered()) { + if (post_jvmti_events && JvmtiExport::should_post_monitor_contended_entered()) { // We are going to call thaw again after this and finish the VMTS // transition so no need to do it here. We will post the event there. current->set_contended_entered_monitor(this); @@ -610,7 +601,8 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito // states will still report that the thread is blocked trying to // acquire it. // If there is a suspend request, ExitOnSuspend will exit the OM - // and set the OM as pending. + // and set the OM as pending, the thread will not be reported as + // having "-locked" the monitor. } if (!eos.exited()) { // ExitOnSuspend did not exit the OM @@ -644,7 +636,7 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito // spinning we could increment JVMStat counters, etc. DTRACE_MONITOR_PROBE(contended__entered, this, object(), current); - if (JvmtiExport::should_post_monitor_contended_entered()) { + if (post_jvmti_events && JvmtiExport::should_post_monitor_contended_entered()) { JvmtiExport::post_monitor_contended_entered(current, this); // The current thread already owns the monitor and is not going to @@ -1102,11 +1094,10 @@ void ObjectMonitor::enter_internal(JavaThread* current) { void ObjectMonitor::reenter_internal(JavaThread* current, ObjectWaiter* currentNode) { assert(current != nullptr, "invariant"); - assert(current->thread_state() != _thread_blocked, "invariant"); + assert(current->thread_state() == _thread_blocked, "invariant"); assert(currentNode != nullptr, "invariant"); assert(currentNode->_thread == current, "invariant"); assert(_waiters > 0, "invariant"); - assert_mark_word_consistency(); // If there are unmounted virtual threads ahead in the _entry_list we want // to do a timed-park instead to alleviate some deadlock cases where one @@ -1142,22 +1133,15 @@ void ObjectMonitor::reenter_internal(JavaThread* current, ObjectWaiter* currentN { OSThreadContendState osts(current->osthread()); - - assert(current->thread_state() == _thread_in_vm, "invariant"); - - { - ClearSuccOnSuspend csos(this); - ThreadBlockInVMPreprocess tbivs(current, csos, true /* allow_suspend */); - if (do_timed_parked) { - current->_ParkEvent->park(recheck_interval); - // Increase the recheck_interval, but clamp the value. - recheck_interval *= 8; - if (recheck_interval > MAX_RECHECK_INTERVAL) { - recheck_interval = MAX_RECHECK_INTERVAL; - } - } else { - current->_ParkEvent->park(); + if (do_timed_parked) { + current->_ParkEvent->park(recheck_interval); + // Increase the recheck_interval, but clamp the value. + recheck_interval *= 8; + if (recheck_interval > MAX_RECHECK_INTERVAL) { + recheck_interval = MAX_RECHECK_INTERVAL; } + } else { + current->_ParkEvent->park(); } } @@ -1184,7 +1168,6 @@ void ObjectMonitor::reenter_internal(JavaThread* current, ObjectWaiter* currentN // Current has acquired the lock -- Unlink current from the _entry_list. assert(has_owner(current), "invariant"); - assert_mark_word_consistency(); unlink_after_acquire(current, currentNode); if (has_successor(current)) clear_successor(); assert(!has_successor(current), "invariant"); @@ -1883,7 +1866,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // while (!timeout && !interrupted && node.TState == TS_WAIT) park() int ret = OS_OK; - bool was_notified = false; + bool was_notified = true; // Need to check interrupt state whilst still _thread_in_vm bool interrupted = interruptible && current->is_interrupted(false); @@ -1895,8 +1878,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { assert(current->thread_state() == _thread_in_vm, "invariant"); { - ClearSuccOnSuspend csos(this); - ThreadBlockInVMPreprocess tbivs(current, csos, true /* allow_suspend */); + ThreadBlockInVM tbivm(current, false /* allow_suspend */); if (interrupted || HAS_PENDING_EXCEPTION) { // Intentionally empty } else if (node.TState == ObjectWaiter::TS_WAIT) { @@ -1928,17 +1910,16 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { if (node.TState == ObjectWaiter::TS_WAIT) { dequeue_specific_waiter(&node); // unlink from wait_set node.TState = ObjectWaiter::TS_RUN; + was_notified = false; } } - - // The thread is now either on off-list (TS_RUN), + // The thread is now either off-list (TS_RUN), // or on the entry_list (TS_ENTER). // The Node's TState variable is stable from the perspective of this thread. // No other threads will asynchronously modify TState. guarantee(node.TState != ObjectWaiter::TS_WAIT, "invariant"); OrderAccess::loadload(); if (has_successor(current)) clear_successor(); - was_notified = node.TState == ObjectWaiter::TS_ENTER; // Reentry phase -- reacquire the monitor. // re-enter contended monitor after object.wait(). @@ -1947,27 +1928,19 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // although the raw address of the object may have changed. // (Don't cache naked oops over safepoints, of course). - // post monitor waited event. Note that this is past-tense, we are done waiting. - if (JvmtiExport::should_post_monitor_waited()) { - JvmtiExport::post_monitor_waited(current, this, ret == OS_TIMEOUT); + // Post monitor waited event. Note that this is past-tense, we are done waiting. + // An event could have been enabled after notification, in this case + // a thread will have TS_ENTER state and posting the event may hit a suspension point. + // From a debugging perspective, it is more important to have no missing events. + if (interruptible && JvmtiExport::should_post_monitor_waited() && node.TState != ObjectWaiter::TS_ENTER) { - if (was_notified && has_successor(current)) { - // In this part of the monitor wait-notify-reenter protocol it - // is possible (and normal) for another thread to do a fastpath - // monitor enter-exit while this thread is still trying to get - // to the reenter portion of the protocol. - // - // The ObjectMonitor was notified and the current thread is - // the successor which also means that an unpark() has already - // been done. The JVMTI_EVENT_MONITOR_WAITED event handler can - // consume the unpark() that was done when the successor was - // set because the same ParkEvent is shared between Java - // monitors and JVM/TI RawMonitors (for now). - // - // We redo the unpark() to ensure forward progress, i.e., we - // don't want all pending threads hanging (parked) with none - // entering the unlocked monitor. - current->_ParkEvent->unpark(); + // Process suspend requests now if any, before posting the event. + { + ThreadBlockInVM tbvm(current, true); + } + // Re-check the condition as the monitor waited events can be disabled whilst thread was suspended. + if (JvmtiExport::should_post_monitor_waited()) { + JvmtiExport::post_monitor_waited(current, this, ret == OS_TIMEOUT); } } @@ -1986,8 +1959,30 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { NoPreemptMark npm(current); enter(current); } else { + // This means the thread has been un-parked and added to the entry_list + // in notify_internal, i.e. notified while waiting. guarantee(v == ObjectWaiter::TS_ENTER, "invariant"); - reenter_internal(current, &node); + ExitOnSuspend eos(this); + { + ThreadBlockInVMPreprocess tbivs(current, eos, true /* allow_suspend */); + reenter_internal(current, &node); + // We can go to a safepoint at the end of this block. If we + // do a thread dump during that safepoint, then this thread will show + // as having "-locked" the monitor, but the OS and java.lang.Thread + // states will still report that the thread is blocked trying to + // acquire it. + // If there is a suspend request, ExitOnSuspend will exit the OM + // and set the OM as pending, the thread will not be reported as + // having "-locked" the monitor. + } + if (eos.exited()) { + // ExitOnSuspend exit the OM + assert(!has_owner(current), "invariant"); + guarantee(node.TState == ObjectWaiter::TS_RUN, "invariant"); + current->set_current_pending_monitor(nullptr); + enter(current, false /* post_jvmti_events */); + } + assert(has_owner(current), "invariant"); node.wait_reenter_end(this); } @@ -2041,6 +2036,8 @@ bool ObjectMonitor::notify_internal(JavaThread* current) { ObjectWaiter* iterator = dequeue_waiter(); if (iterator != nullptr) { guarantee(iterator->TState == ObjectWaiter::TS_WAIT, "invariant"); + iterator->_notifier_tid = JFR_THREAD_ID(current); + did_notify = true; if (iterator->is_vthread()) { oop vthread = iterator->vthread(); @@ -2056,45 +2053,55 @@ bool ObjectMonitor::notify_internal(JavaThread* current) { old_state == java_lang_VirtualThread::TIMED_WAIT) { java_lang_VirtualThread::cmpxchg_state(vthread, old_state, java_lang_VirtualThread::BLOCKED); } - // Increment counter *before* adding the vthread to the _entry_list. - // Adding to _entry_list uses Atomic::cmpxchg() which already provides - // a fence that prevents reordering of the stores. - inc_unmounted_vthreads(); + if (!JvmtiExport::should_post_monitor_waited()) { + // Increment counter *before* adding the vthread to the _entry_list. + // Adding to _entry_list uses Atomic::cmpxchg() which already provides + // a fence that prevents reordering of the stores. + inc_unmounted_vthreads(); + add_to_entry_list(current, iterator); + } else { + iterator->TState = ObjectWaiter::TS_RUN; + if (java_lang_VirtualThread::set_onWaitingList(vthread, vthread_list_head())) { + ParkEvent* pe = ObjectMonitor::vthread_unparker_ParkEvent(); + pe->unpark(); + } + } + } else { + if (!JvmtiExport::should_post_monitor_waited()) { + add_to_entry_list(current, iterator); + // Read counter *after* adding the thread to the _entry_list. + // Adding to _entry_list uses Atomic::cmpxchg() which already provides + // a fence that prevents this load from floating up previous store. + if (has_unmounted_vthreads()) { + // Wake up the thread to alleviate some deadlock cases where the successor + // that will be picked up when this thread releases the monitor is an unmounted + // virtual thread that cannot run due to having run out of carriers. Upon waking + // up, the thread will call reenter_internal() which will use timed-park in case + // there is contention and there are still vthreads in the _entry_list. + // If the target was interrupted or the wait timed-out at the same time, it could + // have reached reenter_internal and read a false value of has_unmounted_vthreads() + // before we added it to the _entry_list above. To deal with that case, we set _do_timed_park + // which will be read by the target on the next loop iteration in reenter_internal. + iterator->_do_timed_park = true; + JavaThread* t = iterator->thread(); + t->_ParkEvent->unpark(); + } + iterator->wait_reenter_begin(this); + } else { + iterator->TState = ObjectWaiter::TS_RUN; + JavaThread* t = iterator->thread(); + assert(t != nullptr, ""); + t->_ParkEvent->unpark(); + } } - iterator->_notifier_tid = JFR_THREAD_ID(current); - did_notify = true; - add_to_entry_list(current, iterator); - // _wait_set_lock protects the wait queue, not the entry_list. We could // move the add-to-entry_list operation, above, outside the critical section // protected by _wait_set_lock. In practice that's not useful. With the - // exception of wait() timeouts and interrupts the monitor owner + // exception of wait() timeouts and interrupts the monitor owner // is the only thread that grabs _wait_set_lock. There's almost no contention // on _wait_set_lock so it's not profitable to reduce the length of the // critical section. - - if (!iterator->is_vthread()) { - iterator->wait_reenter_begin(this); - - // Read counter *after* adding the thread to the _entry_list. - // Adding to _entry_list uses Atomic::cmpxchg() which already provides - // a fence that prevents this load from floating up previous store. - if (has_unmounted_vthreads()) { - // Wake up the thread to alleviate some deadlock cases where the successor - // that will be picked up when this thread releases the monitor is an unmounted - // virtual thread that cannot run due to having run out of carriers. Upon waking - // up, the thread will call reenter_internal() which will use timed-park in case - // there is contention and there are still vthreads in the _entry_list. - // If the target was interrupted or the wait timed-out at the same time, it could - // have reached reenter_internal and read a false value of has_unmounted_vthreads() - // before we added it to the _entry_list above. To deal with that case, we set _do_timed_park - // which will be read by the target on the next loop iteration in reenter_internal. - iterator->_do_timed_park = true; - JavaThread* t = iterator->thread(); - t->_ParkEvent->unpark(); - } - } } return did_notify; } @@ -2221,19 +2228,22 @@ bool ObjectMonitor::vthread_wait_reenter(JavaThread* current, ObjectWaiter* node // The first time we run after being preempted on Object.wait() we // need to check if we were interrupted or the wait timed-out, and // in that case remove ourselves from the _wait_set queue. + bool was_notified = true; if (node->TState == ObjectWaiter::TS_WAIT) { SpinCriticalSection scs(&_wait_set_lock); if (node->TState == ObjectWaiter::TS_WAIT) { dequeue_specific_waiter(node); // unlink from wait_set node->TState = ObjectWaiter::TS_RUN; + was_notified = false; } } // If this was an interrupted case, set the _interrupted boolean so that // once we re-acquire the monitor we know if we need to throw IE or not. ObjectWaiter::TStates state = node->TState; - bool was_notified = state == ObjectWaiter::TS_ENTER; - assert(was_notified || state == ObjectWaiter::TS_RUN, ""); + assert(was_notified || state == ObjectWaiter::TS_RUN, + "was not notified and is not in the right state: state = %s", + node->getTStateName(state)); node->_interrupted = node->_interruptible && !was_notified && current->is_interrupted(false); // Post JFR and JVMTI events. If non-interruptible we are in @@ -2246,7 +2256,10 @@ bool ObjectMonitor::vthread_wait_reenter(JavaThread* current, ObjectWaiter* node // Mark that we are at reenter so that we don't call this method again. node->_at_reenter = true; - if (!was_notified) { + // We check the state rather than was_notified because, when JVMTI + // monitor_waited event is enabled, the notifier only unparks the waiter + // without adding it to the entry_list. + if (state == ObjectWaiter::TS_RUN) { bool acquired = vthread_monitor_enter(current, node); if (acquired) { guarantee(_recursions == 0, "invariant"); @@ -2537,6 +2550,23 @@ ObjectWaiter::ObjectWaiter(JavaThread* current) { _active = false; } +const char* ObjectWaiter::getTStateName(ObjectWaiter::TStates state) { + switch (state) { + case ObjectWaiter::TS_UNDEF: + return "TS_UNDEF"; + case ObjectWaiter::TS_READY: + return "TS_READY"; + case ObjectWaiter::TS_RUN: + return "TS_RUN"; + case ObjectWaiter::TS_WAIT: + return "TS_WAIT"; + case ObjectWaiter::TS_ENTER: + return "TS_ENTER"; + default: + ShouldNotReachHere(); + } +} + ObjectWaiter::ObjectWaiter(oop vthread, ObjectMonitor* mon) : ObjectWaiter(nullptr) { assert(oopDesc::is_oop(vthread), ""); _vthread = OopHandle(JavaThread::thread_oop_storage(), vthread); diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index 53b64f1e8a5..574a652f230 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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 @@ -40,7 +40,6 @@ class ParkEvent; class BasicLock; class ContinuationWrapper; - class ObjectWaiter : public CHeapObj { public: enum TStates : uint8_t { TS_UNDEF, TS_READY, TS_RUN, TS_WAIT, TS_ENTER }; @@ -72,7 +71,7 @@ class ObjectWaiter : public CHeapObj { oop vthread() const; void wait_reenter_begin(ObjectMonitor *mon); void wait_reenter_end(ObjectMonitor *mon); - + const char* getTStateName(TStates state); void set_bad_pointers() { #ifdef ASSERT this->_prev = (ObjectWaiter*) badAddressVal; @@ -352,7 +351,6 @@ class ObjectMonitor : public CHeapObj { // returns false and throws IllegalMonitorStateException (IMSE). bool check_owner(TRAPS); - private: class ExitOnSuspend { protected: ObjectMonitor* _om; @@ -362,23 +360,16 @@ class ObjectMonitor : public CHeapObj { void operator()(JavaThread* current); bool exited() { return _om_exited; } }; - class ClearSuccOnSuspend { - protected: - ObjectMonitor* _om; - public: - ClearSuccOnSuspend(ObjectMonitor* om) : _om(om) {} - void operator()(JavaThread* current); - }; bool enter_is_async_deflating(); - void notify_contended_enter(JavaThread *current); + void notify_contended_enter(JavaThread *current, bool post_jvmti_events = true); public: void enter_for_with_contention_mark(JavaThread* locking_thread, ObjectMonitorContentionMark& contention_mark); bool enter_for(JavaThread* locking_thread); - bool enter(JavaThread* current); + bool enter(JavaThread* current, bool post_jvmti_events = true); bool try_enter(JavaThread* current, bool check_for_recursion = true); bool spin_enter(JavaThread* current); - void enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark& contention_mark); + void enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark& contention_mark, bool post_jvmti_events = true); void exit(JavaThread* current, bool not_suspended = true); bool resume_operation(JavaThread* current, ObjectWaiter* node, ContinuationWrapper& cont); void wait(jlong millis, bool interruptible, TRAPS); diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 1c06bf3c521..57971897400 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -2211,8 +2211,8 @@ bool os::used_memory(physical_memory_size_type& value) { bool os::Machine::used_memory(physical_memory_size_type& value) { physical_memory_size_type avail_mem = 0; // Return value ignored - defaulting to 0 on failure. - (void)os::available_memory(avail_mem); - physical_memory_size_type phys_mem = os::physical_memory(); + (void)os::Machine::available_memory(avail_mem); + physical_memory_size_type phys_mem = os::Machine::physical_memory(); value = phys_mem - avail_mem; return true; } diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp index 140207e5d63..11bd39c839f 100644 --- a/src/hotspot/share/runtime/sharedRuntime.hpp +++ b/src/hotspot/share/runtime/sharedRuntime.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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,7 +30,6 @@ #include "code/vmreg.hpp" #include "interpreter/linkResolver.hpp" #include "memory/allStatic.hpp" -#include "memory/metaspaceClosure.hpp" #include "memory/resourceArea.hpp" #include "runtime/safepointVerifiers.hpp" #include "runtime/stubInfo.hpp" @@ -38,6 +37,7 @@ class AdapterHandlerEntry; class AdapterFingerPrint; +class MetaspaceClosure; class vframeStream; // Runtime is the base class for various runtime interfaces diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 4ecc8f9ca01..f65a3441bf4 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -79,6 +79,7 @@ #include "oops/typeArrayOop.hpp" #include "prims/jvmtiAgentThread.hpp" #include "runtime/arguments.hpp" +#include "runtime/atomic.hpp" #include "runtime/deoptimization.hpp" #include "runtime/flags/jvmFlag.hpp" #include "runtime/globals.hpp" @@ -888,6 +889,7 @@ declare_unsigned_integer_type(unsigned short) \ declare_unsigned_integer_type(jushort) \ declare_unsigned_integer_type(unsigned long) \ + declare_unsigned_integer_type(Atomic) \ /* The compiler thinks this is a different type than */ \ /* unsigned short on Win32 */ \ declare_unsigned_integer_type(u1) \ @@ -901,6 +903,7 @@ /*****************************/ \ \ declare_toplevel_type(void*) \ + declare_toplevel_type(Atomic) \ declare_toplevel_type(int*) \ declare_toplevel_type(char*) \ declare_toplevel_type(char**) \ diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp index 1fef271c8c0..e300bea6993 100644 --- a/src/hotspot/share/utilities/growableArray.hpp +++ b/src/hotspot/share/utilities/growableArray.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -116,6 +116,12 @@ protected: ~GrowableArrayView() {} +protected: + // Used by AOTGrowableArray for MetaspaceClosure support. + E** data_addr() { + return &_data; + } + public: bool operator==(const GrowableArrayView& rhs) const { if (_len != rhs._len) @@ -487,16 +493,16 @@ public: return false; } - // Remove all elements up to the index (exclusive). The order is preserved. - void remove_till(int idx) { - remove_range(0, idx); + // Remove all elements in the range [0; end). The order is preserved. + void remove_till(int end) { + remove_range(0, end); } - // Remove all elements in the range [start - end). The order is preserved. + // Remove all elements in the range [start; end). The order is preserved. void remove_range(int start, int end) { assert(0 <= start, "illegal start index %d", start); - assert(start < end && end <= this->_len, - "erase called with invalid range (%d, %d) for length %d", + assert(start <= end && end <= this->_len, + "erase called with invalid range [%d, %d) for length %d", start, end, this->_len); for (int i = start, j = end; j < this->length(); i++, j++) { diff --git a/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m b/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m index 31572eaeb81..6d8eb832370 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, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, 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 @@ -134,10 +134,11 @@ static OSStatus completeCertChain( CFArrayAppendValue(certArray, identity); /* the single element in certs-to-be-evaluated comes from the identity */ - ortn = SecIdentityCopyCertificate(identity, &certRef); - if(ortn) { + ortn = SecIdentityCopyCertificate(identity, &certRef); + if (ortn) { /* should never happen */ cssmPerror("SecIdentityCopyCertificate", ortn); + CFRelease(certArray); return ortn; } @@ -283,6 +284,7 @@ static void addIdentitiesToKeystore(JNIEnv *env, jobject keyStore, jmethodID jm_ OSStatus err = SecIdentitySearchCreate(NULL, 0, &identitySearch); SecIdentityRef theIdentity = NULL; OSErr searchResult = noErr; + CFArrayRef certChain = NULL; do { searchResult = SecIdentitySearchCopyNext(identitySearch, &theIdentity); @@ -291,7 +293,6 @@ static void addIdentitiesToKeystore(JNIEnv *env, jobject keyStore, jmethodID jm_ // Get the cert from the identity, then generate a chain. SecCertificateRef certificate; SecIdentityCopyCertificate(theIdentity, &certificate); - CFArrayRef certChain = NULL; // *** Should do something with this error... err = completeCertChain(theIdentity, NULL, TRUE, &certChain); @@ -357,6 +358,11 @@ static void addIdentitiesToKeystore(JNIEnv *env, jobject keyStore, jmethodID jm_ if ((*env)->ExceptionCheck(env)) { goto errOut; } + + if (certChain != NULL) { + CFRelease(certChain); + certChain = NULL; + } } } while (searchResult == noErr); @@ -364,6 +370,9 @@ errOut: if (identitySearch != NULL) { CFRelease(identitySearch); } + if (certChain != NULL) { + CFRelease(certChain); + } } #define ADD(list, str) { \ diff --git a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java index 1280ccdad74..bf6576f4d93 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java @@ -1353,22 +1353,16 @@ public final class ML_KEM { } } - // The intrinsic implementations assume that the input and output buffers - // are such that condensed can be read in 96-byte chunks and - // parsed can be written in 64 shorts chunks except for the last chunk - // that can be either 48 or 64 shorts. In other words, - // if (i - 1) * 64 < parsedLengths <= i * 64 then - // parsed.length should be either i * 64 or (i-1) * 64 + 48 and - // condensed.length should be at least index + i * 96. + // An intrinsic implementation assumes that the input and output buffers + // are such that condensed can be read in chunks of 192 bytes and + // parsed can be written in chunks of 128 shorts, so callers should allocate + // the condensed and parsed arrays accordingly, see the assert() private void twelve2Sixteen(byte[] condensed, int index, short[] parsed, int parsedLength) { - int i = parsedLength / 64; - int remainder = parsedLength - i * 64; - if (remainder != 0) { - i++; - } - assert ((remainder == 0) || (remainder == 48)) && - (index + i * 96 <= condensed.length); + int n = (parsedLength + 127) / 128; + assert ((parsed.length >= n * 128) && + (condensed.length >= index + n * 192)); + implKyber12To16(condensed, index, parsed, parsedLength); } diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeInfo.java index f6f5f9928bc..e1cc52bfa1d 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeInfo.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -124,7 +124,7 @@ public sealed interface CharacterRangeInfo *
  • {@link CharacterRange#FLAG_BRANCH_TRUE} A condition encoded * in the branch instruction immediately contained in the code range for * this item is not inverted towards the corresponding branch condition in - * the source code. I.e. actual jump occurs if and only if the the source + * the source code. I.e. actual jump occurs if and only if the source * code branch condition evaluates to true. Entries of this type are * produced only for conditions that are listed in the description of * CRT_FLOW_CONTROLLER flag. The source range for the entry contains flow @@ -136,7 +136,7 @@ public sealed interface CharacterRangeInfo *
  • {@link CharacterRange#FLAG_BRANCH_FALSE} A condition encoded * in the branch instruction immediately contained in the code range for * this item is inverted towards the corresponding branch condition in the - * source code. I.e. actual jump occurs if and only if the the source code + * source code. I.e. actual jump occurs if and only if the source code * branch condition evaluates to false. Entries of this type are produced * only for conditions that are listed in the description of * CRT_FLOW_CONTROLLER flag. The source range for the entry contains flow diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java index feb8aaaa1a9..7fde9d1dd89 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, 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 @@ -4826,7 +4826,9 @@ assert((int)twice.invokeExact(21) == 42); * Before the method handle is returned, the passed-in value is converted to the requested type. * If the requested type is primitive, widening primitive conversions are attempted, * else reference conversions are attempted. - *

    The returned method handle is equivalent to {@code identity(type).bindTo(value)}. + *

    The returned method handle is equivalent to {@code identity(type).bindTo(value)}, + * for reference types. For all types it is equivalent to + * {@code insertArguments(identity(type), 0, value)}. * @param type the return type of the desired method handle * @param value the value to return * @return a method handle of the given return type and no arguments, which always returns the given value diff --git a/src/java.base/share/classes/java/lang/runtime/ExactConversionsSupport.java b/src/java.base/share/classes/java/lang/runtime/ExactConversionsSupport.java index a1a93859c42..8d4389f7295 100644 --- a/src/java.base/share/classes/java/lang/runtime/ExactConversionsSupport.java +++ b/src/java.base/share/classes/java/lang/runtime/ExactConversionsSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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,12 +64,9 @@ package java.lang.runtime; * floating-point type is considered exact.

  • * * - * @see - * JLS 5.7.1 Exact Testing Conversions - * @see - * JLS 5.7.2 Unconditionally Exact Testing Conversions - * @see - * JLS 15.20.2 The instanceof Operator + * @jls primitive-types-in-patterns-instanceof-switch-5.7.1 Exact Testing Conversions + * @jls primitive-types-in-patterns-instanceof-switch-5.7.2 Unconditionally Exact Testing Conversions + * @jls primitive-types-in-patterns-instanceof-switch-15.20.2 The {@code instanceof} Operator * * @implNote Some exactness checks describe a test which can be redirected * safely through one of the existing methods. Those are omitted too (i.e., diff --git a/src/java.base/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java b/src/java.base/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java index 192b1f7958b..4e6cadc737c 100644 --- a/src/java.base/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java +++ b/src/java.base/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2026, 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 @@ -208,7 +208,7 @@ public abstract class AsynchronousServerSocketChannel *

    The {@code backlog} parameter is the maximum number of pending * connections on the socket. Its exact semantics are implementation specific. * In particular, an implementation may impose a maximum length or may choose - * to ignore the parameter altogther. If the {@code backlog} parameter has + * to ignore the parameter altogether. If the {@code backlog} parameter has * the value {@code 0}, or a negative value, then an implementation specific * default is used. * diff --git a/src/java.base/share/classes/java/nio/charset/Charset-X-Coder.java.template b/src/java.base/share/classes/java/nio/charset/Charset-X-Coder.java.template index e900c2eca0f..aca987ed678 100644 --- a/src/java.base/share/classes/java/nio/charset/Charset-X-Coder.java.template +++ b/src/java.base/share/classes/java/nio/charset/Charset-X-Coder.java.template @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -819,6 +819,12 @@ public abstract class Charset$Coder$ { */ public final $Otype$Buffer $code$($Itype$Buffer in) throws CharacterCodingException + { + return $code$(in, true); + } + + private $Otype$Buffer $code$($Itype$Buffer in, boolean throwOnError) + throws CharacterCodingException { int n = Math.min((int)(in.remaining() * average$ItypesPerOtype$()), ArraysSupport.SOFT_MAX_ARRAY_LENGTH); @@ -844,7 +850,11 @@ public abstract class Charset$Coder$ { out = o; continue; } - cr.throwException(); + if (throwOnError) { + cr.throwException(); + } else { + return null; + } } out.flip(); return out; @@ -938,7 +948,8 @@ public abstract class Charset$Coder$ { try { onMalformedInput(CodingErrorAction.REPORT); onUnmappableCharacter(CodingErrorAction.REPORT); - encode(cb); + ByteBuffer bb = encode(cb, false); + return bb != null; } catch (CharacterCodingException x) { return false; } finally { @@ -946,7 +957,6 @@ public abstract class Charset$Coder$ { onUnmappableCharacter(ua); reset(); } - return true; } /** diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java index 16d7193c556..9368cf54afd 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, 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 @@ -817,6 +817,7 @@ public final class DateTimeFormatter { *

  • The {@link #ISO_LOCAL_DATE} *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. + * The offset parsing is lenient, which allows the minutes and seconds to be optional. * Parsing is case insensitive. * *

    @@ -829,7 +830,9 @@ public final class DateTimeFormatter { ISO_OFFSET_DATE = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_DATE) + .parseLenient() .appendOffsetId() + .parseStrict() .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE); } @@ -846,6 +849,7 @@ public final class DateTimeFormatter { *

  • If the offset is not available then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. + * The offset parsing is lenient, which allows the minutes and seconds to be optional. * Parsing is case insensitive. * *

    @@ -862,7 +866,9 @@ public final class DateTimeFormatter { .parseCaseInsensitive() .append(ISO_LOCAL_DATE) .optionalStart() + .parseLenient() .appendOffsetId() + .parseStrict() .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE); } @@ -919,6 +925,7 @@ public final class DateTimeFormatter { *

  • The {@link #ISO_LOCAL_TIME} *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. + * The offset parsing is lenient, which allows the minutes and seconds to be optional. * Parsing is case insensitive. * *

    @@ -930,7 +937,9 @@ public final class DateTimeFormatter { ISO_OFFSET_TIME = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_TIME) + .parseLenient() .appendOffsetId() + .parseStrict() .toFormatter(ResolverStyle.STRICT, null); } @@ -947,6 +956,7 @@ public final class DateTimeFormatter { *

  • If the offset is not available then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. + * The offset parsing is lenient, which allows the minutes and seconds to be optional. * Parsing is case insensitive. * *

    @@ -962,7 +972,9 @@ public final class DateTimeFormatter { .parseCaseInsensitive() .append(ISO_LOCAL_TIME) .optionalStart() + .parseLenient() .appendOffsetId() + .parseStrict() .toFormatter(ResolverStyle.STRICT, null); } @@ -1075,6 +1087,7 @@ public final class DateTimeFormatter { *

  • If the offset is not available to format or parse then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. + * The offset parsing is lenient, which allows the minutes and seconds to be optional. *
  • If the zone ID is not available or is a {@code ZoneOffset} then the format is complete. *
  • An open square bracket '['. *
  • The {@link ZoneId#getId() zone ID}. This is not part of the ISO-8601 standard. @@ -1094,7 +1107,9 @@ public final class DateTimeFormatter { ISO_DATE_TIME = new DateTimeFormatterBuilder() .append(ISO_LOCAL_DATE_TIME) .optionalStart() + .parseLenient() .appendOffsetId() + .parseStrict() .optionalStart() .appendLiteral('[') .parseCaseSensitive() @@ -1121,6 +1136,7 @@ public final class DateTimeFormatter { *
  • If the offset is not available to format or parse then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. + * The offset parsing is lenient, which allows the minutes and seconds to be optional. * Parsing is case insensitive. * *

    @@ -1139,7 +1155,9 @@ public final class DateTimeFormatter { .appendLiteral('-') .appendValue(DAY_OF_YEAR, 3) .optionalStart() + .parseLenient() .appendOffsetId() + .parseStrict() .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE); } @@ -1165,6 +1183,7 @@ public final class DateTimeFormatter { *

  • If the offset is not available to format or parse then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. + * The offset parsing is lenient, which allows the minutes and seconds to be optional. * Parsing is case insensitive. * *

    @@ -1185,7 +1204,9 @@ public final class DateTimeFormatter { .appendLiteral('-') .appendValue(DAY_OF_WEEK, 1) .optionalStart() + .parseLenient() .appendOffsetId() + .parseStrict() .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE); } diff --git a/src/java.base/share/classes/java/util/Optional.java b/src/java.base/share/classes/java/util/Optional.java index 3e577bd379c..3d0375c4354 100644 --- a/src/java.base/share/classes/java/util/Optional.java +++ b/src/java.base/share/classes/java/util/Optional.java @@ -25,7 +25,7 @@ package java.util; -import jdk.internal.vm.annotation.Stable; +import jdk.internal.vm.annotation.TrustFinalFields; import java.util.function.Consumer; import java.util.function.Function; @@ -62,6 +62,7 @@ import java.util.stream.Stream; * @since 1.8 */ @jdk.internal.ValueBased +@TrustFinalFields public final class Optional { /** * Common instance for {@code empty()}. @@ -71,7 +72,6 @@ public final class Optional { /** * If non-null, the value; if null, indicates no value is present */ - @Stable private final T value; /** diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java index 2250009e8f5..70acb8a0889 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java @@ -42,6 +42,8 @@ import java.util.function.IntUnaryOperator; import jdk.internal.misc.Unsafe; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; +import jdk.internal.vm.annotation.TrustFinalFields; + import java.lang.invoke.VarHandle; /** @@ -371,6 +373,7 @@ public abstract class AtomicIntegerFieldUpdater { /** * Standard hotspot implementation using intrinsics. */ + @TrustFinalFields private static final class AtomicIntegerFieldUpdaterImpl extends AtomicIntegerFieldUpdater { private static final Unsafe U = Unsafe.getUnsafe(); diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java index 5f0a666cb04..d3a3fe63d0f 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java @@ -42,6 +42,8 @@ import java.util.function.LongUnaryOperator; import jdk.internal.misc.Unsafe; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; +import jdk.internal.vm.annotation.TrustFinalFields; + import java.lang.invoke.VarHandle; /** @@ -368,6 +370,7 @@ public abstract class AtomicLongFieldUpdater { return next; } + @TrustFinalFields private static final class CASUpdater extends AtomicLongFieldUpdater { private static final Unsafe U = Unsafe.getUnsafe(); private final long offset; diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java index 4a758f77a47..3d47e8e323a 100644 --- a/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java +++ b/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java @@ -42,6 +42,8 @@ import java.util.function.UnaryOperator; import jdk.internal.misc.Unsafe; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; +import jdk.internal.vm.annotation.TrustFinalFields; + import java.lang.invoke.VarHandle; /** @@ -312,6 +314,7 @@ public abstract class AtomicReferenceFieldUpdater { return next; } + @TrustFinalFields private static final class AtomicReferenceFieldUpdaterImpl extends AtomicReferenceFieldUpdater { private static final Unsafe U = Unsafe.getUnsafe(); 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 7fa507980c2..a198c35c366 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2026, 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 @@ -37,6 +37,7 @@ import java.nio.charset.Charset; import java.nio.file.InvalidPathException; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.Files; +import java.nio.file.attribute.FileTime; import java.util.*; import java.util.function.Consumer; import java.util.function.IntFunction; @@ -86,11 +87,11 @@ public class ZipFile implements ZipConstants, Closeable { private final ZipCoder zipCoder; private volatile boolean closeRequested; - // The "resource" used by this ZIP file that needs to be - // cleaned after use. + // An object holding state which needs to be cleaned after + // this ZipFile is closed or becomes unreachable: // a) the input streams that need to be closed // b) the list of cached Inflater objects - // c) the "native" source of this ZIP file. + // c) the Source object providing read access to the actual ZIP file private final @Stable CleanableResource res; private static final int STORED = ZipEntry.STORED; @@ -1444,11 +1445,13 @@ public class ZipFile implements ZipConstants, Closeable { * The unique combination of these components identifies a Source of a ZipFile. */ private static class Key { - private final BasicFileAttributes attrs; private final File file; + private final Object fileKey; + private final FileTime lastModifiedTime; // the Charset that was provided when constructing the ZipFile instance private final Charset charset; + /** * Constructs a {@code Key} to a {@code Source} of a {@code ZipFile} * @@ -1457,7 +1460,8 @@ public class ZipFile implements ZipConstants, Closeable { * @param charset the Charset that was provided when constructing the ZipFile instance */ public Key(File file, BasicFileAttributes attrs, Charset charset) { - this.attrs = attrs; + this.fileKey = attrs.fileKey(); + this.lastModifiedTime = attrs.lastModifiedTime(); this.file = file; this.charset = charset; } @@ -1465,10 +1469,9 @@ public class ZipFile implements ZipConstants, Closeable { @Override public int hashCode() { long t = charset.hashCode(); - t += attrs.lastModifiedTime().toMillis(); - Object fk = attrs.fileKey(); + t += lastModifiedTime.toMillis(); return Long.hashCode(t) + - (fk != null ? fk.hashCode() : file.hashCode()); + (fileKey != null ? fileKey.hashCode() : file.hashCode()); } @Override @@ -1477,12 +1480,12 @@ public class ZipFile implements ZipConstants, Closeable { if (!charset.equals(key.charset)) { return false; } - if (!attrs.lastModifiedTime().equals(key.attrs.lastModifiedTime())) { + if (!lastModifiedTime.equals(key.lastModifiedTime)) { return false; } - Object fk = attrs.fileKey(); - if (fk != null) { - return fk.equals(key.attrs.fileKey()); + + if (fileKey != null) { + return fileKey.equals(key.fileKey); } else { return file.equals(key.file); } diff --git a/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java b/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java index 3504ce7e8b3..90771575657 100644 --- a/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java +++ b/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -41,7 +41,6 @@ import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; import java.security.CodeSigner; import java.security.cert.Certificate; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -98,11 +97,20 @@ public class URLClassPath { DEBUG_CP_URL_CHECK = p != null ? p.equals("true") || p.isEmpty() : false; } - /* The original search path of URLs. */ - private final ArrayList path; + /* Search path of URLs passed to the constructor or by calls to addURL. + * Access is guarded by a monitor on 'searchPath' itself + */ + private final ArrayList searchPath; - /* The deque of unopened URLs */ - private final ArrayDeque unopenedUrls; + /* Index of the next URL in the search path to process. + * Access is guarded by a monitor on 'searchPath' + */ + private int nextURL = 0; + + /* List of URLs found during expansion of JAR 'Class-Path' attributes. + * Access is guarded by a monitor on 'searchPath' + */ + private final ArrayList manifestClassPath = new ArrayList<>(); /* The resulting search path of Loaders */ private final ArrayList loaders = new ArrayList<>(); @@ -128,14 +136,8 @@ public class URLClassPath { */ public URLClassPath(URL[] urls, URLStreamHandlerFactory factory) { - ArrayList path = new ArrayList<>(urls.length); - ArrayDeque unopenedUrls = new ArrayDeque<>(urls.length); - for (URL url : urls) { - path.add(url); - unopenedUrls.add(url); - } - this.path = path; - this.unopenedUrls = unopenedUrls; + // Reject null URL array or any null element in the array + this.searchPath = new ArrayList<>(List.of(urls)); if (factory != null) { jarHandler = factory.createURLStreamHandler("jar"); @@ -174,16 +176,7 @@ public class URLClassPath { off = next + 1; } while (next != -1); } - - // can't use ArrayDeque#addAll or new ArrayDeque(Collection); - // it's too early in the bootstrap to trigger use of lambdas - int size = path.size(); - ArrayDeque unopenedUrls = new ArrayDeque<>(size); - for (int i = 0; i < size; i++) - unopenedUrls.add(path.get(i)); - - this.unopenedUrls = unopenedUrls; - this.path = path; + this.searchPath = path; // the application class loader uses the built-in protocol handler to avoid protocol // handler lookup when opening JAR files on the class path. this.jarHandler = new sun.net.www.protocol.jar.Handler(); @@ -215,10 +208,9 @@ public class URLClassPath { public synchronized void addURL(URL url) { if (closed || url == null) return; - synchronized (unopenedUrls) { - if (! path.contains(url)) { - unopenedUrls.addLast(url); - path.add(url); + synchronized (searchPath) { + if (! searchPath.contains(url)) { + searchPath.add(url); } } } @@ -249,8 +241,8 @@ public class URLClassPath { * Returns the original search path of URLs. */ public URL[] getURLs() { - synchronized (unopenedUrls) { - return path.toArray(new URL[0]); + synchronized (searchPath) { + return searchPath.toArray(new URL[0]); } } @@ -379,6 +371,23 @@ public class URLClassPath { }; } + /* + * Returns the next URL to process or null if finished + */ + private URL nextURL() { + synchronized (searchPath) { + // Check paths discovered during 'Class-Path' expansion first + if (!manifestClassPath.isEmpty()) { + return manifestClassPath.removeLast(); + } + // Check the regular search path + if (nextURL < searchPath.size()) { + return searchPath.get(nextURL++); + } + // All paths exhausted + return null; + } + } /* * Returns the Loader at the specified position in the URL search * path. The URLs are opened and expanded as needed. Returns null @@ -389,14 +398,13 @@ public class URLClassPath { return null; } // Expand URL search path until the request can be satisfied - // or unopenedUrls is exhausted. + // or all paths are exhausted. while (loaders.size() < index + 1) { - final URL url; - synchronized (unopenedUrls) { - url = unopenedUrls.pollFirst(); - if (url == null) - return null; + final URL url = nextURL(); + if (url == null) { + return null; } + // Skip this URL if it already has a Loader. String urlNoFragString = URLUtil.urlNoFragString(url); if (lmap.containsKey(urlNoFragString)) { @@ -422,7 +430,7 @@ public class URLClassPath { continue; } if (loaderClassPathURLs != null) { - push(loaderClassPathURLs); + addManifestClassPaths(loaderClassPathURLs); } // Finally, add the Loader to the search path. loaders.add(loader); @@ -475,13 +483,12 @@ public class URLClassPath { } /* - * Pushes the specified URLs onto the head of unopened URLs. + * Adds the specified URLs to the list of 'Class-Path' expanded URLs */ - private void push(URL[] urls) { - synchronized (unopenedUrls) { - for (int i = urls.length - 1; i >= 0; --i) { - unopenedUrls.addFirst(urls[i]); - } + private void addManifestClassPaths(URL[] urls) { + synchronized (searchPath) { + // Adding in reversed order since manifestClassPath is consumed tail-first + manifestClassPath.addAll(Arrays.asList(urls).reversed()); } } diff --git a/src/java.base/share/classes/jdk/internal/vm/annotation/TrustFinalFields.java b/src/java.base/share/classes/jdk/internal/vm/annotation/TrustFinalFields.java new file mode 100644 index 00000000000..a94f58159a2 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/vm/annotation/TrustFinalFields.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025, 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.vm.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/// Indicates all instance final fields declared in the annotated class should +/// be trusted as constants by compilers in `ciField::is_constant`. +/// +/// The compiler already treats static final fields and instance final fields in +/// record classes and hidden classes as constant. All classes in select +/// packages (Defined in `trust_final_non_static_fields` in `ciField.cpp`) in +/// the boot class loader also have their instance final fields trusted. This +/// annotation is not necessary in these cases. +/// +/// The [Stable] annotation treats fields as constants once they are not the +/// zero or null value. In comparison, a non-stable final instance field +/// trusted by this annotation can treat zero and null values as constants. +/// +/// This annotation is suitable when constant treatment of final fields is +/// performance sensitive, yet package-wide final field constant treatment may +/// be at risk from final field modifications such as serialization. +/// +/// This annotation is only recognized on classes from the boot and platform +/// class loaders and is ignored elsewhere. +/// +/// @since 26 +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface TrustFinalFields { +} diff --git a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java index 2a4dbdc95ed..0aaae14bbf9 100644 --- a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -583,6 +583,16 @@ public class DoubleByte { return encodeChar(c) != UNMAPPABLE_ENCODING; } + public boolean canEncode(CharSequence cs) { + int length = cs.length(); + for (int i = 0; i < length; i++) { + if (!canEncode(cs.charAt(i))) { + return false; + } + } + return true; + } + protected Surrogate.Parser sgp() { if (sgp == null) sgp = new Surrogate.Parser(); diff --git a/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java b/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java index 39215bfa93d..9240ac3f380 100644 --- a/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java +++ b/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -136,6 +136,16 @@ public class ISO_8859_1 return c <= '\u00FF'; } + public boolean canEncode(CharSequence cs) { + int length = cs.length(); + for (int i = 0; i < length; i++) { + if (!canEncode(cs.charAt(i))) { + return false; + } + } + return true; + } + public boolean isLegalReplacement(byte[] repl) { return true; // we accept any byte value } diff --git a/src/java.base/share/classes/sun/nio/cs/SingleByte.java b/src/java.base/share/classes/sun/nio/cs/SingleByte.java index 59887b944d3..d4127b7c043 100644 --- a/src/java.base/share/classes/sun/nio/cs/SingleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/SingleByte.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, 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 @@ -201,6 +201,16 @@ public class SingleByte return encode(c) != UNMAPPABLE_ENCODING; } + public boolean canEncode(CharSequence cs) { + int length = cs.length(); + for (int i = 0; i < length; i++) { + if (!canEncode(cs.charAt(i))) { + return false; + } + } + return true; + } + public boolean isLegalReplacement(byte[] repl) { return ((repl.length == 1 && repl[0] == (byte)'?') || super.isLegalReplacement(repl)); diff --git a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java index bb84ab1bd4b..61c4948e949 100644 --- a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java +++ b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, 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 @@ -139,6 +139,16 @@ public class US_ASCII return c < 0x80; } + public boolean canEncode(CharSequence cs) { + int length = cs.length(); + for (int i = 0; i < length; i++) { + if (!canEncode(cs.charAt(i))) { + return false; + } + } + return true; + } + public boolean isLegalReplacement(byte[] repl) { return (repl.length == 1 && repl[0] >= 0) || super.isLegalReplacement(repl); diff --git a/src/java.base/share/man/keytool.md b/src/java.base/share/man/keytool.md index 19ba8c34912..1d70bd2f5f8 100644 --- a/src/java.base/share/man/keytool.md +++ b/src/java.base/share/man/keytool.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1998, 2026, 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 @@ -211,7 +211,7 @@ perform. ## Commands for Creating or Adding Data to the Keystore -`-gencert` +[`-gencert`]{#command-gencert} : The following are the available options for the `-gencert` command: - {`-rfc`}: Output in RFC (Request For Comment) style @@ -328,7 +328,7 @@ perform. > `keytool -alias e1 -certreq | keytool -alias ca2 -gencert > e1.cert` -`-genkeypair` +[`-genkeypair`]{#option-genkeypair} : The following are the available options for the `-genkeypair` command: - {`-alias` *alias*}: Alias name of the entry to process @@ -478,7 +478,7 @@ perform. specified by `-startdate`, or the current date when `-startdate` isn't specified) for which the certificate should be considered valid. -`-genseckey` +[`-genseckey`]{#command-genseckey} : The following are the available options for the `-genseckey` command: - {`-alias` *alias*}: Alias name of the entry to process @@ -521,7 +521,7 @@ perform. the same password that is used for the `-keystore`. The `-keypass` value must contain at least six characters. -`-importcert` +[`-importcert`]{#command-importcert} : The following are the available options for the `-importcert` command: - {`-noprompt`}: Do not prompt @@ -586,7 +586,7 @@ perform. entry, then the `keytool` command assumes that you're importing a certificate reply. -`-importpass` +[`-importpass`]{#command-importpass} : The following are the available options for the `-importpass` command: - {`-alias` *alias*}: Alias name of the entry to process @@ -629,7 +629,7 @@ perform. ## Commands for Importing Contents from Another Keystore -`-importkeystore` +[`-importkeystore`]{#command-importkeystore} : The following are the available options for the `-importkeystore` command: - `-srckeystore` *keystore*: Source keystore name @@ -724,7 +724,7 @@ perform. ## Commands for Generating a Certificate Request -`-certreq` +[`-certreq`]{#command-certreq} : The following are the available options for the `-certreq` command: - {`-alias` *alias*}: Alias name of the entry to process @@ -786,7 +786,7 @@ perform. ## Commands for Exporting Data -`-exportcert` +[`-exportcert`]{#command-exportcert} : The following are the available options for the `-exportcert` command: - {`-rfc`}: Output in RFC style @@ -834,7 +834,7 @@ perform. ## Commands for Displaying Data -`-list` +[`-list`]{#command-list} : The following are the available options for the `-list` command: - {`-rfc`}: Output in RFC style @@ -881,7 +881,7 @@ perform. You can't specify both `-v` and `-rfc` in the same command. Otherwise, an error is reported. -`-printcert` +[`-printcert`]{#command-printcert} : The following are the available options for the `-printcert` command: - {`-rfc`}: Output in RFC style @@ -946,7 +946,7 @@ perform. trusted certificate in the user keystore (specified by `-keystore`) or in the `cacerts` keystore (if `-trustcacerts` is specified). -`-printcertreq` +[`-printcertreq`]{#command-printcertreq} : The following are the available options for the `-printcertreq` command: - {`-file` *file*}: Input file name @@ -958,7 +958,7 @@ perform. command. The command reads the request from file. If there is no file, then the request is read from the standard input. -`-printcrl` +[`-printcrl`]{#command-printcrl} : The following are the available options for the `-printcrl` command: - {`-file crl`}: Input file name @@ -999,7 +999,7 @@ perform. ## Commands for Managing the Keystore -`-storepasswd` +[`-storepasswd`]{#command-storepasswd} : The following are the available options for the `-storepasswd` command: - \[`-new` *arg*\]: New password @@ -1029,7 +1029,7 @@ perform. integrity of the keystore contents. The new password is set by `-new` *arg* and must contain at least six characters. -`-keypasswd` +[`-keypasswd`]{#command-keypasswd} : The following are the available options for the `-keypasswd` command: - {`-alias` *alias*}: Alias name of the entry to process @@ -1069,7 +1069,7 @@ perform. If the `-new` option isn't provided at the command line, then the user is prompted for it. -`-delete` +[`-delete`]{#command-delete} : The following are the available options for the `-delete` command: - \[`-alias` *alias*\]: Alias name of the entry to process @@ -1101,7 +1101,7 @@ perform. keystore. When not provided at the command line, the user is prompted for the `alias`. -`-changealias` +[`-changealias`]{#command-changealias} : The following are the available options for the `-changealias` command: - {`-alias` *alias*}: Alias name of the entry to process @@ -1143,7 +1143,7 @@ perform. ## Commands for Displaying Security-related Information -`-showinfo` +[`-showinfo`]{#command-showinfo} : The following are the available options for the `-showinfo` command: - {`-tls`}: Displays TLS configuration information @@ -1185,10 +1185,10 @@ environment or memory usage. For a list of possible interpreter options, enter These options can appear for all commands operating on a keystore: -`-storetype` *storetype* +[`-storetype`]{#option-storetype} *storetype* : This qualifier specifies the type of keystore to be instantiated. -`-keystore` *keystore* +[`-keystore`]{#option-keystore} *keystore* : The keystore location. If the JKS `storetype` is used and a keystore file doesn't yet exist, then @@ -1206,13 +1206,13 @@ These options can appear for all commands operating on a keystore: if the keystore isn't file-based. For example, when the keystore resides on a hardware token device. -`-cacerts` *cacerts* +[`-cacerts`]{#option-cacerts} *cacerts* : Operates on the *cacerts* keystore . This option is equivalent to `-keystore` *path\_to\_cacerts* `-storetype` *type\_of\_cacerts*. An error is reported if the `-keystore` or `-storetype` option is used with the `-cacerts` option. -`-storepass` \[`:env` \| `:file` \] *argument* +[`-storepass`]{#option-storepass} \[`:env` \| `:file` \] *argument* : The password that is used to protect the integrity of the keystore. If the modifier `env` or `file` isn't specified, then the password has the @@ -1237,22 +1237,22 @@ These options can appear for all commands operating on a keystore: a password is not specified, then the integrity of the retrieved information can't be verified and a warning is displayed. -`-providername` *name* +[`-providername`]{#option-providername} *name* : Used to identify a cryptographic service provider's name when listed in the security properties file. -`-addprovider` *name* +[`-addprovider`]{#option-addprovider} *name* : Used to add a security provider by name (such as SunPKCS11) . -`-providerclass` *class* +[`-providerclass`]{#option-providerclass} *class* : Used to specify the name of a cryptographic service provider's master class file when the service provider isn't listed in the security properties file. -`-providerpath` *list* +[`-providerpath`]{#option-providerpath} *list* : Used to specify the provider classpath. -`-providerarg` *arg* +[`-providerarg`]{#option-providerarg} *arg* : Used with the `-addprovider` or `-providerclass` option to represent an optional string input argument for the constructor of *class* name. @@ -1263,7 +1263,7 @@ These options can appear for all commands operating on a keystore: following two options, `-srcprotected` and `-destprotected`, are provided for the source keystore and the destination keystore respectively. -`-ext` {*name*{`:critical`} {`=`*value*}} +[`-ext`]{#option-ext} {*name*{`:critical`} {`=`*value*}} : Denotes an X.509 certificate extension. The option can be used in `-genkeypair` and `-gencert` to embed extensions into the generated certificate, or in `-certreq` to show what extensions are requested in the @@ -1276,7 +1276,7 @@ These options can appear for all commands operating on a keystore: `isCritical` attribute is `true`; otherwise, it is `false`. You can use `:c` in place of `:critical`. -`-conf` *file* +[`-conf`]{#option-conf} *file* : Specifies a pre-configured options file. ## Pre-configured options file diff --git a/src/java.base/share/native/libjimage/endian.cpp b/src/java.base/share/native/libjimage/endian.cpp index 5e13ffba34e..73d8aefe086 100644 --- a/src/java.base/share/native/libjimage/endian.cpp +++ b/src/java.base/share/native/libjimage/endian.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -104,6 +104,3 @@ void Endian::set_java(u1* p, u2 x) { p[1] = x & 0xff; } -Endian* Endian::get_native_handler() { - return NativeEndian::get_native(); -} diff --git a/src/java.base/share/native/libjimage/endian.hpp b/src/java.base/share/native/libjimage/endian.hpp index 42fbbb0e853..38e566b7524 100644 --- a/src/java.base/share/native/libjimage/endian.hpp +++ b/src/java.base/share/native/libjimage/endian.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -74,9 +74,6 @@ public: // Select an appropriate endian handler. static Endian* get_handler(bool big_endian); - // Return the native endian handler. - static Endian* get_native_handler(); - // get platform u2 from Java Big endian static u2 get_java(u1* x); // set platform u2 to Java Big endian diff --git a/src/java.base/share/native/libjimage/imageDecompressor.cpp b/src/java.base/share/native/libjimage/imageDecompressor.cpp index 748bbf8203f..4946e645c55 100644 --- a/src/java.base/share/native/libjimage/imageDecompressor.cpp +++ b/src/java.base/share/native/libjimage/imageDecompressor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -85,10 +85,6 @@ void ImageDecompressor::image_decompressor_init() { } } -void ImageDecompressor::image_decompressor_close() { - delete[] _decompressors; -} - /* * Locate decompressor. */ diff --git a/src/java.base/share/native/libjimage/imageDecompressor.hpp b/src/java.base/share/native/libjimage/imageDecompressor.hpp index 16f354935c3..709e1a3bb21 100644 --- a/src/java.base/share/native/libjimage/imageDecompressor.hpp +++ b/src/java.base/share/native/libjimage/imageDecompressor.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -105,12 +105,12 @@ private: protected: ImageDecompressor(const char* name) : _name(name) { } + virtual void decompress_resource(u1* data, u1* uncompressed, ResourceHeader* header, const ImageStrings* strings) = 0; public: static void image_decompressor_init(); - static void image_decompressor_close(); static ImageDecompressor* get_decompressor(const char * decompressor_name) ; static void decompress_resource(u1* compressed, u1* uncompressed, u8 uncompressed_size, const ImageStrings* strings, Endian* _endian); @@ -166,6 +166,6 @@ private: public: SharedStringDecompressor(const char* sym) : ImageDecompressor(sym){} void decompress_resource(u1* data, u1* uncompressed, ResourceHeader* header, - const ImageStrings* strings); + const ImageStrings* strings); }; #endif // LIBJIMAGE_IMAGEDECOMPRESSOR_HPP diff --git a/src/java.base/share/native/libjimage/imageFile.cpp b/src/java.base/share/native/libjimage/imageFile.cpp index d97a8f95a60..e2479ba2c9e 100644 --- a/src/java.base/share/native/libjimage/imageFile.cpp +++ b/src/java.base/share/native/libjimage/imageFile.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -180,16 +180,6 @@ void ImageFileReaderTable::remove(ImageFileReader* image) { } } -// Determine if image entry is in table. -bool ImageFileReaderTable::contains(ImageFileReader* image) { - for (u4 i = 0; i < _count; i++) { - if (_table[i] == image) { - return true; - } - } - return false; -} - // Table to manage multiple opens of an image file. ImageFileReaderTable ImageFileReader::_reader_table; @@ -261,25 +251,6 @@ void ImageFileReader::close(ImageFileReader *reader) { } } -// Return an id for the specified ImageFileReader. -u8 ImageFileReader::reader_to_ID(ImageFileReader *reader) { - // ID is just the cloaked reader address. - return (u8)reader; -} - -// Validate the image id. -bool ImageFileReader::id_check(u8 id) { - // Make sure the ID is a managed (_reader_table) reader. - SimpleCriticalSectionLock cs(&_reader_table_lock); - return _reader_table.contains((ImageFileReader*)id); -} - -// Return an id for the specified ImageFileReader. -ImageFileReader* ImageFileReader::id_to_reader(u8 id) { - assert(id_check(id) && "invalid image id"); - return (ImageFileReader*)id; -} - // Constructor initializes to a closed state. ImageFileReader::ImageFileReader(const char* name, bool big_endian) { // Copy the image file name. @@ -372,23 +343,6 @@ bool ImageFileReader::read_at(u1* data, u8 size, u8 offset) const { return (u8)osSupport::read(_fd, (char*)data, size, offset) == size; } -// Find the location attributes associated with the path. Returns true if -// the location is found, false otherwise. -bool ImageFileReader::find_location(const char* path, ImageLocation& location) const { - // Locate the entry in the index perfect hash table. - s4 index = ImageStrings::find(_endian, path, _redirect_table, table_length()); - // If is found. - if (index != ImageStrings::NOT_FOUND) { - // Get address of first byte of location attribute stream. - u1* data = get_location_data(index); - // Expand location attributes. - location.set_data(data); - // Make sure result is not a false positive. - return verify_location(location, path); - } - return false; -} - // Find the location index and size associated with the path. // Returns the location index and size if the location is found, 0 otherwise. u4 ImageFileReader::find_location_index(const char* path, u8 *size) const { diff --git a/src/java.base/share/native/libjimage/imageFile.hpp b/src/java.base/share/native/libjimage/imageFile.hpp index 5fb4ea3baaa..a4c8d159efa 100644 --- a/src/java.base/share/native/libjimage/imageFile.hpp +++ b/src/java.base/share/native/libjimage/imageFile.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -375,9 +375,6 @@ public: // Remove an image entry from the table. void remove(ImageFileReader* image); - - // Determine if image entry is in table. - bool contains(ImageFileReader* image); }; // Manage the image file. @@ -445,15 +442,6 @@ public: // Close an image file if the file is not in use elsewhere. static void close(ImageFileReader *reader); - // Return an id for the specified ImageFileReader. - static u8 reader_to_ID(ImageFileReader *reader); - - // Validate the image id. - static bool id_check(u8 id); - - // Return an id for the specified ImageFileReader. - static ImageFileReader* id_to_reader(u8 id); - // Open image file for read access. bool open(); @@ -545,10 +533,6 @@ public: return _endian->get(_offsets_table[index]); } - // Find the location attributes associated with the path. Returns true if - // the location is found, false otherwise. - bool find_location(const char* path, ImageLocation& location) const; - // Find the location index and size associated with the path. // Returns the location index and size if the location is found, // ImageFileReader::NOT_FOUND otherwise. diff --git a/src/java.base/unix/native/libnet/net_util_md.c b/src/java.base/unix/native/libnet/net_util_md.c index 1496be5f5c6..48cc1a7bb02 100644 --- a/src/java.base/unix/native/libnet/net_util_md.c +++ b/src/java.base/unix/native/libnet/net_util_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -68,13 +68,14 @@ NET_ThrowByNameWithLastError(JNIEnv *env, const char *name, void NET_ThrowNew(JNIEnv *env, int errorNumber, char *msg) { char fullMsg[512]; - if (!msg) { - msg = "no further information"; - } switch(errorNumber) { case EBADF: - jio_snprintf(fullMsg, sizeof(fullMsg), "socket closed: %s", msg); - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", fullMsg); + if (msg == NULL) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed"); + } else { + jio_snprintf(fullMsg, sizeof(fullMsg), "socket closed: %s", msg); + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", fullMsg); + } break; default: errno = errorNumber; diff --git a/src/java.base/windows/native/libnet/net_util_md.c b/src/java.base/windows/native/libnet/net_util_md.c index bac3d1438ab..5abdd8d4c2e 100644 --- a/src/java.base/windows/native/libnet/net_util_md.c +++ b/src/java.base/windows/native/libnet/net_util_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -139,13 +139,6 @@ NET_ThrowNew(JNIEnv *env, int errorNum, char *msg) return; } - /* - * Default message text if not provided - */ - if (!msg) { - msg = "no further information"; - } - /* * Check table for known winsock errors */ @@ -163,13 +156,22 @@ NET_ThrowNew(JNIEnv *env, int errorNum, char *msg) */ if (i < table_size) { excP = (char *)winsock_errors[i].exc; - jio_snprintf(fullMsg, sizeof(fullMsg), "%s: %s", - (char *)winsock_errors[i].errString, msg); + if (msg == NULL) { + jio_snprintf(fullMsg, sizeof(fullMsg), "%s", + (char *)winsock_errors[i].errString); + } else { + jio_snprintf(fullMsg, sizeof(fullMsg), "%s: %s", + (char *)winsock_errors[i].errString, msg); + } } else { - jio_snprintf(fullMsg, sizeof(fullMsg), - "Unrecognized Windows Sockets error: %d: %s", - errorNum, msg); - + if (msg == NULL) { + jio_snprintf(fullMsg, sizeof(fullMsg), + "Unrecognized Windows Sockets error: %d", errorNum); + } else { + jio_snprintf(fullMsg, sizeof(fullMsg), + "Unrecognized Windows Sockets error: %d: %s", + errorNum, msg); + } } /* diff --git a/src/java.base/windows/native/libnio/ch/Net.c b/src/java.base/windows/native/libnio/ch/Net.c index 814f502c48a..adfd67b5017 100644 --- a/src/java.base/windows/native/libnio/ch/Net.c +++ b/src/java.base/windows/native/libnio/ch/Net.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -733,7 +733,7 @@ Java_sun_nio_ch_Net_pollConnect(JNIEnv* env, jclass this, jobject fdo, jlong tim NET_ThrowNew(env, lastError, "getsockopt"); } } else if (optError != NO_ERROR) { - NET_ThrowNew(env, optError, "getsockopt"); + NET_ThrowNew(env, optError, NULL); } return JNI_FALSE; } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java index 24a1997bdc1..5145779a493 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java @@ -1421,6 +1421,8 @@ public class GTKLookAndFeel extends SynthLookAndFeel { return c.getComponentOrientation().isLeftToRight(); } + private native boolean applyThemeIfNeeded(); + /** * {@inheritDoc} */ @@ -1463,6 +1465,21 @@ public class GTKLookAndFeel extends SynthLookAndFeel { // By default mnemonics are hidden for GTK L&F MnemonicHandler.setMnemonicHidden(true); + + if (IS_3) { + boolean shouldApplyTheme = false; + if (toolkit instanceof UNIXToolkit unixToolkit) { + if ("gnome".equals(unixToolkit.getDesktop())) { + int gnomeShellVersion = unixToolkit.getGnomeShellMajorVersion(); + shouldApplyTheme = gnomeShellVersion >= 47; + } + } + + if (shouldApplyTheme && applyThemeIfNeeded()) { + GTKEngine.INSTANCE.themeChanged(); + GTKIconFactory.resetIcons(); + } + } } /** diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java index baeb249eaa8..76d7abdbce6 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKStyle.java @@ -32,7 +32,6 @@ import javax.swing.*; import javax.swing.plaf.*; import javax.swing.plaf.synth.*; -import sun.awt.AppContext; import sun.awt.UNIXToolkit; import sun.swing.SwingUtilities2; import javax.swing.plaf.synth.SynthIcon; @@ -961,11 +960,11 @@ class GTKStyle extends SynthStyle implements GTKConstants { static class GTKStockIconInfo { private static Map ICON_TYPE_MAP; - private static final Object ICON_SIZE_KEY = new StringBuffer("IconSize"); + + private static Dimension[] iconSizesMap; private static Dimension[] getIconSizesMap() { - AppContext appContext = AppContext.getAppContext(); - Dimension[] iconSizes = (Dimension[])appContext.get(ICON_SIZE_KEY); + Dimension[] iconSizes = iconSizesMap; if (iconSizes == null) { iconSizes = new Dimension[7]; @@ -976,7 +975,7 @@ class GTKStyle extends SynthStyle implements GTKConstants { iconSizes[4] = new Dimension(20, 20); // GTK_ICON_SIZE_BUTTON iconSizes[5] = new Dimension(32, 32); // GTK_ICON_SIZE_DND iconSizes[6] = new Dimension(48, 48); // GTK_ICON_SIZE_DIALOG - appContext.put(ICON_SIZE_KEY, iconSizes); + iconSizesMap = iconSizes; } return iconSizes; } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java index 0e4c2e526ac..7a11e4a47e9 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java @@ -42,8 +42,6 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicButtonListener; import javax.swing.plaf.basic.BasicButtonUI; -import sun.awt.AppContext; - /** * MotifButton implementation * @@ -55,20 +53,13 @@ public class MotifButtonUI extends BasicButtonUI { private boolean defaults_initialized = false; - private static final Object MOTIF_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new MotifButtonUI(); // ******************************** // Create PLAF // ******************************** public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - MotifButtonUI motifButtonUI = - (MotifButtonUI) appContext.get(MOTIF_BUTTON_UI_KEY); - if (motifButtonUI == null) { - motifButtonUI = new MotifButtonUI(); - appContext.put(MOTIF_BUTTON_UI_KEY, motifButtonUI); - } - return motifButtonUI; + return UI; } // ******************************** diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java index 9e9f0008b07..7017eaf546c 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java @@ -30,8 +30,6 @@ import javax.swing.JComponent; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; -import sun.awt.AppContext; - /** * MotifCheckBox implementation * @@ -39,7 +37,7 @@ import sun.awt.AppContext; */ public class MotifCheckBoxUI extends MotifRadioButtonUI { - private static final Object MOTIF_CHECK_BOX_UI_KEY = new Object(); + private static final ComponentUI UI = new MotifCheckBoxUI(); private static final String propertyPrefix = "CheckBox" + "."; @@ -50,14 +48,7 @@ public class MotifCheckBoxUI extends MotifRadioButtonUI { // Create PLAF // ******************************** public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - MotifCheckBoxUI motifCheckBoxUI = - (MotifCheckBoxUI) appContext.get(MOTIF_CHECK_BOX_UI_KEY); - if (motifCheckBoxUI == null) { - motifCheckBoxUI = new MotifCheckBoxUI(); - appContext.put(MOTIF_CHECK_BOX_UI_KEY, motifCheckBoxUI); - } - return motifCheckBoxUI; + return UI; } @Override diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLabelUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLabelUI.java index f19a3c23cb5..89c2e309f9e 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLabelUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLabelUI.java @@ -29,8 +29,6 @@ import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicLabelUI; -import sun.awt.AppContext; - /** * A Motif {@literal L&F} implementation of LabelUI. * This merely sets up new default values in MotifLookAndFeel. @@ -39,16 +37,9 @@ import sun.awt.AppContext; */ public class MotifLabelUI extends BasicLabelUI { - private static final Object MOTIF_LABEL_UI_KEY = new Object(); + private static final ComponentUI UI = new MotifLabelUI(); public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - MotifLabelUI motifLabelUI = - (MotifLabelUI) appContext.get(MOTIF_LABEL_UI_KEY); - if (motifLabelUI == null) { - motifLabelUI = new MotifLabelUI(); - appContext.put(MOTIF_LABEL_UI_KEY, motifLabelUI); - } - return motifLabelUI; + return UI; } } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java index 7997145e648..7956023ed06 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java @@ -39,8 +39,6 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicToggleButtonUI; -import sun.awt.AppContext; - /** * BasicToggleButton implementation. * @@ -48,7 +46,7 @@ import sun.awt.AppContext; */ public class MotifToggleButtonUI extends BasicToggleButtonUI { - private static final Object MOTIF_TOGGLE_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new MotifToggleButtonUI(); protected Color selectColor; @@ -58,14 +56,7 @@ public class MotifToggleButtonUI extends BasicToggleButtonUI // Create PLAF // ******************************** public static ComponentUI createUI(JComponent b) { - AppContext appContext = AppContext.getAppContext(); - MotifToggleButtonUI motifToggleButtonUI = - (MotifToggleButtonUI) appContext.get(MOTIF_TOGGLE_BUTTON_UI_KEY); - if (motifToggleButtonUI == null) { - motifToggleButtonUI = new MotifToggleButtonUI(); - appContext.put(MOTIF_TOGGLE_BUTTON_UI_KEY, motifToggleButtonUI); - } - return motifToggleButtonUI; + return UI; } // ******************************** diff --git a/src/java.desktop/share/classes/java/awt/image/Raster.java b/src/java.desktop/share/classes/java/awt/image/Raster.java index 053e7c1eec5..3312a39b693 100644 --- a/src/java.desktop/share/classes/java/awt/image/Raster.java +++ b/src/java.desktop/share/classes/java/awt/image/Raster.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -452,6 +452,9 @@ public class Raster { throw new IllegalArgumentException("Dimensions (width="+w+ " height="+h+") are too large"); } + if (scanlineStride < 0) { + throw new IllegalArgumentException("Scanline stride must be >= 0"); + } if (bankIndices == null) { throw new ArrayIndexOutOfBoundsException("Bank indices array is null"); diff --git a/src/java.desktop/share/classes/javax/swing/ImageIcon.java b/src/java.desktop/share/classes/javax/swing/ImageIcon.java index aaaa2dfd4a4..ee6c08ebb15 100644 --- a/src/java.desktop/share/classes/javax/swing/ImageIcon.java +++ b/src/java.desktop/share/classes/javax/swing/ImageIcon.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -54,7 +54,6 @@ import javax.accessibility.AccessibleState; import javax.accessibility.AccessibleStateSet; import sun.awt.AWTAccessor; -import sun.awt.AppContext; /** * An implementation of the Icon interface that paints Icons @@ -110,18 +109,7 @@ public class ImageIcon implements Icon, Serializable, Accessible { * @deprecated since 1.8 */ @Deprecated - protected static final Component component = createComponent(); - - private static final Component createComponent() { - try { - Component component = new Component() {}; - // 6482575 - clear the appContext field so as not to leak it - AWTAccessor.getComponentAccessor().setAppContext(component, null); - return component; - } catch (Throwable t) { - return null; - } - } + protected static final Component component = new Component() {}; /** * Do not use this shared media tracker, which is used to load images. @@ -136,8 +124,6 @@ public class ImageIcon implements Icon, Serializable, Accessible { */ private static int mediaTrackerID; - private static final Object TRACKER_KEY = new StringBuilder("TRACKER_KEY"); - int width = -1; int height = -1; @@ -346,24 +332,12 @@ public class ImageIcon implements Icon, Serializable, Accessible { } } + private static final MediaTracker MEDIA_TRACKER = new MediaTracker(new Component() {}); /** - * Returns the MediaTracker for the current AppContext, creating a new - * MediaTracker if necessary. + * Returns the shared MediaTracker. */ private MediaTracker getTracker() { - Object trackerObj; - AppContext ac = AppContext.getAppContext(); - // Opt: Only synchronize if trackerObj comes back null? - // If null, synchronize, re-check for null, and put new tracker - synchronized(ac) { - trackerObj = ac.get(TRACKER_KEY); - if (trackerObj == null) { - Component comp = new Component() {}; - trackerObj = new MediaTracker(comp); - ac.put(TRACKER_KEY, trackerObj); - } - } - return (MediaTracker) trackerObj; + return MEDIA_TRACKER; } /** diff --git a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java index 38bde1a97fa..1b04e8c6169 100644 --- a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java +++ b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -960,7 +960,9 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { if (oldInvoker != null) { oldInvoker.removePropertyChangeListener("ancestor", propListener); } - invoker.addPropertyChangeListener("ancestor", propListener); + if (invoker != null) { + invoker.addPropertyChangeListener("ancestor", propListener); + } ui.installUI(this); } invalidate(); diff --git a/src/java.desktop/share/classes/javax/swing/JTable.java b/src/java.desktop/share/classes/javax/swing/JTable.java index 6e64b216d58..f5c914135d1 100644 --- a/src/java.desktop/share/classes/javax/swing/JTable.java +++ b/src/java.desktop/share/classes/javax/swing/JTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -3191,9 +3191,23 @@ public class JTable extends JComponent implements TableModelListener, Scrollable * (maximum or minimum). * */ + public void doLayout() { + boolean prefWidthSet = false; TableColumn resizingColumn = getResizingColumn(); - if (resizingColumn == null) { + // doLayout is called for both pack and show + // so if initial preferred width is set by user then + // it needs to be honoured even if resizingColumn + // is set to last column on account of + // AUTO_RESIZE_LAST_COLUMN autoResizeMode + for (int i = 0; i < columnModel.getColumnCount(); i++) { + if (columnModel.getColumn(i).getPreferredWidth() != 75 + && columnModel.getColumn(i).getWidth() == 75) { + prefWidthSet = true; + break; + } + } + if (resizingColumn == null || prefWidthSet) { setWidthsFromPreferredWidths(false); } else { diff --git a/src/java.desktop/share/classes/javax/swing/LayoutStyle.java b/src/java.desktop/share/classes/javax/swing/LayoutStyle.java index f1c384db6e2..bf84eb2379b 100644 --- a/src/java.desktop/share/classes/javax/swing/LayoutStyle.java +++ b/src/java.desktop/share/classes/javax/swing/LayoutStyle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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,6 @@ package javax.swing; import java.awt.Container; import javax.swing.plaf.ComponentUI; -import sun.awt.AppContext; /** * LayoutStyle provides information about how to position @@ -40,6 +39,9 @@ import sun.awt.AppContext; * @since 1.6 */ public abstract class LayoutStyle { + + private static LayoutStyle sharedInstance; + /** * Sets the shared instance of LayoutStyle. Specifying * null results in using the LayoutStyle from @@ -51,10 +53,10 @@ public abstract class LayoutStyle { public static void setInstance(LayoutStyle style) { synchronized(LayoutStyle.class) { if (style == null) { - AppContext.getAppContext().remove(LayoutStyle.class); + sharedInstance = null; } else { - AppContext.getAppContext().put(LayoutStyle.class, style); + sharedInstance = style; } } } @@ -70,8 +72,7 @@ public abstract class LayoutStyle { public static LayoutStyle getInstance() { LayoutStyle style; synchronized(LayoutStyle.class) { - style = (LayoutStyle)AppContext.getAppContext(). - get(LayoutStyle.class); + style = sharedInstance; } if (style == null) { return UIManager.getLookAndFeel().getLayoutStyle(); diff --git a/src/java.desktop/share/classes/javax/swing/UIManager.java b/src/java.desktop/share/classes/javax/swing/UIManager.java index 7a2ae226f33..69063c562e6 100644 --- a/src/java.desktop/share/classes/javax/swing/UIManager.java +++ b/src/java.desktop/share/classes/javax/swing/UIManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -59,6 +59,7 @@ import java.util.Objects; import sun.awt.AppContext; import sun.awt.AWTAccessor; +import sun.swing.SwingAccessor; /** * {@code UIManager} manages the current look and feel, the set of @@ -233,8 +234,9 @@ public class UIManager implements Serializable */ public UIManager() {} + private static final LAFState LAF_STATE = new LAFState(); /** - * Return the LAFState object, lazily create one if necessary. + * Return the LAFState object. * All access to the LAFState fields is done via this method, * for example: *

    @@ -242,22 +244,18 @@ public class UIManager implements Serializable
          * 
    */ private static LAFState getLAFState() { - LAFState rv = (LAFState)SwingUtilities.appContextGet( - SwingUtilities2.LAF_STATE_KEY); - if (rv == null) { - synchronized (classLock) { - rv = (LAFState)SwingUtilities.appContextGet( - SwingUtilities2.LAF_STATE_KEY); - if (rv == null) { - SwingUtilities.appContextPut( - SwingUtilities2.LAF_STATE_KEY, - (rv = new LAFState())); - } - } + synchronized (classLock) { + return LAF_STATE; } - return rv; } + static { + SwingAccessor.setLAFStateAccessor(UIManager::isLafStateInitialized); + } + + private static boolean isLafStateInitialized() { + return LAF_STATE.initialized; + } /* Keys used in the swing.properties properties file. * See loadUserProperties(), initialize(). diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonUI.java index c7c036e859f..7846e04b565 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -59,7 +59,6 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; import javax.swing.text.View; -import sun.awt.AppContext; import sun.swing.SwingUtilities2; /** @@ -88,7 +87,7 @@ public class BasicButtonUI extends ButtonUI{ private static final String propertyPrefix = "Button" + "."; - private static final Object BASIC_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new BasicButtonUI(); private KeyListener keyListener = null; @@ -107,14 +106,7 @@ public class BasicButtonUI extends ButtonUI{ * @return an instance of {@code BasicButtonUI} */ public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - BasicButtonUI buttonUI = - (BasicButtonUI) appContext.get(BASIC_BUTTON_UI_KEY); - if (buttonUI == null) { - buttonUI = new BasicButtonUI(); - appContext.put(BASIC_BUTTON_UI_KEY, buttonUI); - } - return buttonUI; + return UI; } /** diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicCheckBoxUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicCheckBoxUI.java index 24af28c94c0..49549521005 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicCheckBoxUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicCheckBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -25,8 +25,6 @@ package javax.swing.plaf.basic; -import sun.awt.AppContext; - import javax.swing.*; import java.awt.*; @@ -51,7 +49,7 @@ import java.io.Serializable; */ public class BasicCheckBoxUI extends BasicRadioButtonUI { - private static final Object BASIC_CHECK_BOX_UI_KEY = new Object(); + private static final ComponentUI UI = new BasicCheckBoxUI(); private static final String propertyPrefix = "CheckBox" + "."; @@ -71,14 +69,7 @@ public class BasicCheckBoxUI extends BasicRadioButtonUI { * @return an instance of {@code BasicCheckBoxUI} */ public static ComponentUI createUI(JComponent b) { - AppContext appContext = AppContext.getAppContext(); - BasicCheckBoxUI checkboxUI = - (BasicCheckBoxUI) appContext.get(BASIC_CHECK_BOX_UI_KEY); - if (checkboxUI == null) { - checkboxUI = new BasicCheckBoxUI(); - appContext.put(BASIC_CHECK_BOX_UI_KEY, checkboxUI); - } - return checkboxUI; + return UI; } public String getPropertyPrefix() { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java index 2d1a458e0d7..de3cb1adf75 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -34,7 +34,6 @@ import javax.swing.text.*; import javax.swing.event.*; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; -import sun.awt.AppContext; import sun.swing.DefaultLookup; import sun.swing.SwingUtilities2; import sun.swing.UIAction; @@ -202,10 +201,6 @@ public class BasicComboBoxUI extends ComboBoxUI { // Cached the size that the display needs to render the largest item private Dimension cachedDisplaySize = new Dimension( 0, 0 ); - // Key used for lookup of the DefaultListCellRenderer in the AppContext. - private static final Object COMBO_UI_LIST_CELL_RENDERER_KEY = - new StringBuffer("DefaultListCellRendererKey"); - static final StringBuffer HIDE_POPUP_KEY = new StringBuffer("HidePopupKey"); @@ -237,18 +232,10 @@ public class BasicComboBoxUI extends ComboBoxUI { */ public BasicComboBoxUI() {} + private static final ListCellRenderer CELL_RENDERER = new DefaultListCellRenderer(); // Used for calculating the default size. private static ListCellRenderer getDefaultListCellRenderer() { - @SuppressWarnings("unchecked") - ListCellRenderer renderer = (ListCellRenderer)AppContext. - getAppContext().get(COMBO_UI_LIST_CELL_RENDERER_KEY); - - if (renderer == null) { - renderer = new DefaultListCellRenderer(); - AppContext.getAppContext().put(COMBO_UI_LIST_CELL_RENDERER_KEY, - new DefaultListCellRenderer()); - } - return renderer; + return CELL_RENDERER; } /** diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java index 279de053fd6..04251ca6266 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -28,7 +28,6 @@ package javax.swing.plaf.basic; import sun.swing.SwingUtilities2; import sun.swing.DefaultLookup; import sun.swing.UIAction; -import sun.awt.AppContext; import javax.swing.*; import javax.swing.plaf.*; diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java index 12b871d6efd..964ee1cb9c9 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -78,7 +78,6 @@ import javax.swing.plaf.FontUIResource; import javax.swing.plaf.InsetsUIResource; import javax.swing.text.DefaultEditorKit; -import sun.awt.AppContext; import sun.awt.SunToolkit; import sun.swing.SwingAccessor; import sun.swing.SwingUtilities2; @@ -126,11 +125,6 @@ public abstract class BasicLookAndFeel extends LookAndFeel implements Serializab AWTEventHelper invocator = null; - /* - * Listen for our AppContext being disposed - */ - private PropertyChangeListener disposer = null; - /** * Constructor for subclasses to call. */ @@ -174,18 +168,6 @@ public abstract class BasicLookAndFeel extends LookAndFeel implements Serializab if (invocator == null) { invocator = new AWTEventHelper(); needsEventHelper = true; - - // Add a PropertyChangeListener to our AppContext so we're alerted - // when the AppContext is disposed(), at which time this laf should - // be uninitialize()d. - disposer = new PropertyChangeListener() { - public void propertyChange(PropertyChangeEvent prpChg) { - uninitialize(); - } - }; - AppContext.getAppContext().addPropertyChangeListener( - AppContext.GUI_DISPOSED, - disposer); } } @@ -193,35 +175,21 @@ public abstract class BasicLookAndFeel extends LookAndFeel implements Serializab * {@inheritDoc} */ public void uninitialize() { - AppContext context = AppContext.getAppContext(); - synchronized (BasicPopupMenuUI.MOUSE_GRABBER_KEY) { - Object grabber = context.get(BasicPopupMenuUI.MOUSE_GRABBER_KEY); - if (grabber != null) { - ((BasicPopupMenuUI.MouseGrabber)grabber).uninstall(); - } - } - synchronized (BasicPopupMenuUI.MENU_KEYBOARD_HELPER_KEY) { - Object helper = - context.get(BasicPopupMenuUI.MENU_KEYBOARD_HELPER_KEY); - if (helper != null) { - ((BasicPopupMenuUI.MenuKeyboardHelper)helper).uninstall(); - } - } + synchronized (BasicPopupMenuUI.class) { + if (BasicPopupMenuUI.mouseGrabber != null) { + BasicPopupMenuUI.mouseGrabber.uninstall(); + BasicPopupMenuUI.mouseGrabber = null; + } + if (BasicPopupMenuUI.menuKeyboardHelper != null) { + BasicPopupMenuUI.menuKeyboardHelper.uninstall(); + BasicPopupMenuUI.menuKeyboardHelper = null; + } + } - if(invocator != null) { + if (invocator != null) { invocator.run(); invocator = null; } - - if (disposer != null) { - // Note that we're likely calling removePropertyChangeListener() - // during the course of AppContext.firePropertyChange(). - // However, EventListenerAggregate has code to safely modify - // the list under such circumstances. - context.removePropertyChangeListener(AppContext.GUI_DISPOSED, - disposer); - disposer = null; - } } /** diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java index 6fab795e36c..8df27d3e3cb 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -41,8 +41,6 @@ import java.util.*; import sun.swing.UIAction; -import sun.awt.AppContext; - /** * A Windows L&F implementation of PopupMenuUI. This implementation * is a "combined" view/controller. @@ -52,10 +50,9 @@ import sun.awt.AppContext; * @author Arnaud Weber */ public class BasicPopupMenuUI extends PopupMenuUI { - static final StringBuilder MOUSE_GRABBER_KEY = new StringBuilder( - "javax.swing.plaf.basic.BasicPopupMenuUI.MouseGrabber"); - static final StringBuilder MENU_KEYBOARD_HELPER_KEY = new StringBuilder( - "javax.swing.plaf.basic.BasicPopupMenuUI.MenuKeyboardHelper"); + + static MouseGrabber mouseGrabber = new MouseGrabber(); + static MenuKeyboardHelper menuKeyboardHelper = null; /** * The instance of {@code JPopupMenu}. @@ -126,23 +123,18 @@ public class BasicPopupMenuUI extends PopupMenuUI { } popupMenu.addMenuKeyListener(menuKeyListener); - AppContext context = AppContext.getAppContext(); - synchronized (MOUSE_GRABBER_KEY) { - MouseGrabber mouseGrabber = (MouseGrabber)context.get( - MOUSE_GRABBER_KEY); - if (mouseGrabber == null) { - mouseGrabber = new MouseGrabber(); - context.put(MOUSE_GRABBER_KEY, mouseGrabber); - } + synchronized (MouseGrabber.class) { + if (mouseGrabber == null) mouseGrabber = new MouseGrabber(); } - synchronized (MENU_KEYBOARD_HELPER_KEY) { - MenuKeyboardHelper helper = - (MenuKeyboardHelper)context.get(MENU_KEYBOARD_HELPER_KEY); + + synchronized (BasicPopupMenuUI.class) { + MenuKeyboardHelper helper = menuKeyboardHelper; if (helper == null) { helper = new MenuKeyboardHelper(); - context.put(MENU_KEYBOARD_HELPER_KEY, helper); MenuSelectionManager msm = MenuSelectionManager.defaultManager(); msm.addChangeListener(helper); + menuKeyboardHelper = helper; + } } } @@ -768,10 +760,10 @@ public class BasicPopupMenuUI extends PopupMenuUI { } void uninstall() { - synchronized (MOUSE_GRABBER_KEY) { + synchronized (MouseGrabber.class) { MenuSelectionManager.defaultManager().removeChangeListener(this); ungrabWindow(); - AppContext.getAppContext().remove(MOUSE_GRABBER_KEY); + mouseGrabber = null; } } @@ -1233,9 +1225,8 @@ public class BasicPopupMenuUI extends PopupMenuUI { } void uninstall() { - synchronized (MENU_KEYBOARD_HELPER_KEY) { + synchronized (BasicPopupMenuUI.class) { MenuSelectionManager.defaultManager().removeChangeListener(this); - AppContext.getAppContext().remove(MENU_KEYBOARD_HELPER_KEY); } } } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicRadioButtonUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicRadioButtonUI.java index 9439059e03e..fdc68d3244f 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicRadioButtonUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicRadioButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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,7 +30,6 @@ import javax.swing.*; import javax.swing.plaf.*; import javax.swing.text.View; import sun.swing.SwingUtilities2; -import sun.awt.AppContext; /** * RadioButtonUI implementation for BasicRadioButtonUI @@ -39,7 +38,7 @@ import sun.awt.AppContext; */ public class BasicRadioButtonUI extends BasicToggleButtonUI { - private static final Object BASIC_RADIO_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new BasicRadioButtonUI(); /** * The icon. @@ -66,14 +65,7 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI * @return an instance of {@code BasicRadioButtonUI} */ public static ComponentUI createUI(JComponent b) { - AppContext appContext = AppContext.getAppContext(); - BasicRadioButtonUI radioButtonUI = - (BasicRadioButtonUI) appContext.get(BASIC_RADIO_BUTTON_UI_KEY); - if (radioButtonUI == null) { - radioButtonUI = new BasicRadioButtonUI(); - appContext.put(BASIC_RADIO_BUTTON_UI_KEY, radioButtonUI); - } - return radioButtonUI; + return UI; } @Override diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java index 391cfdf0b65..d144b5b7240 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -41,7 +41,6 @@ import javax.swing.border.Border; import javax.swing.plaf.UIResource; import javax.swing.plaf.synth.SynthUI; import sun.swing.DefaultLookup; -import sun.awt.AppContext; import sun.swing.SwingUtilities2; import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag; @@ -2234,24 +2233,19 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory { } } + private static volatile DragListener dragListenerSingleton; + private static DragListener getDragListener() { synchronized(DragListener.class) { - DragListener listener = - (DragListener)AppContext.getAppContext(). - get(DragListener.class); - - if (listener == null) { - listener = new DragListener(); - AppContext.getAppContext().put(DragListener.class, listener); + if (dragListenerSingleton == null) { + dragListenerSingleton = new DragListener(); } - - return listener; + return dragListenerSingleton; } } /** * Listens for mouse events for the purposes of detecting drag gestures. - * BasicTextUI will maintain one of these per AppContext. */ static class DragListener extends MouseInputAdapter implements BeforeDrag { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToggleButtonUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToggleButtonUI.java index 2b68fe2b4f3..1a7dfe87353 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToggleButtonUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToggleButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -25,8 +25,6 @@ package javax.swing.plaf.basic; -import sun.awt.AppContext; - import java.awt.*; import java.awt.event.*; @@ -44,7 +42,7 @@ import javax.swing.text.View; */ public class BasicToggleButtonUI extends BasicButtonUI { - private static final Object BASIC_TOGGLE_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new BasicToggleButtonUI(); private static final String propertyPrefix = "ToggleButton" + "."; @@ -64,14 +62,7 @@ public class BasicToggleButtonUI extends BasicButtonUI { * @return an instance of {@code BasicToggleButtonUI} */ public static ComponentUI createUI(JComponent b) { - AppContext appContext = AppContext.getAppContext(); - BasicToggleButtonUI toggleButtonUI = - (BasicToggleButtonUI) appContext.get(BASIC_TOGGLE_BUTTON_UI_KEY); - if (toggleButtonUI == null) { - toggleButtonUI = new BasicToggleButtonUI(); - appContext.put(BASIC_TOGGLE_BUTTON_UI_KEY, toggleButtonUI); - } - return toggleButtonUI; + return UI; } protected String getPropertyPrefix() { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/DefaultMetalTheme.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/DefaultMetalTheme.java index 8ee862fb0b3..d64267676c3 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/DefaultMetalTheme.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/DefaultMetalTheme.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, 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,7 +29,7 @@ import javax.swing.plaf.*; import javax.swing.*; import java.awt.*; -import sun.awt.AppContext; +import sun.swing.SwingAccessor; import sun.swing.SwingUtilities2; /** @@ -153,8 +153,7 @@ public class DefaultMetalTheme extends MetalTheme { static int getDefaultFontStyle(int key) { if (key != WINDOW_TITLE_FONT) { Object boldMetal = null; - if (AppContext.getAppContext().get( - SwingUtilities2.LAF_STATE_KEY) != null) { + if (SwingAccessor.getLAFStateAccessor().lafStateIsInitialized()) { // Only access the boldMetal key if a look and feel has // been loaded, otherwise we'll trigger loading the look // and feel. diff --git a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/Effect.java b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/Effect.java index 30bed2207e4..d1b7ba143b3 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/nimbus/Effect.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/nimbus/Effect.java @@ -24,8 +24,6 @@ */ package javax.swing.plaf.nimbus; -import sun.awt.AppContext; - import java.awt.image.BufferedImage; import java.lang.ref.SoftReference; @@ -81,13 +79,10 @@ abstract class Effect { // ================================================================================================================= // Static data cache + private static final ArrayCache ARRAY_CACHE = new ArrayCache(); + protected static ArrayCache getArrayCache() { - ArrayCache cache = (ArrayCache)AppContext.getAppContext().get(ArrayCache.class); - if (cache == null){ - cache = new ArrayCache(); - AppContext.getAppContext().put(ArrayCache.class,cache); - } - return cache; + return ARRAY_CACHE; } protected static class ArrayCache { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/ImagePainter.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/ImagePainter.java index 8a1d957b615..1171f64eb23 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/ImagePainter.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/ImagePainter.java @@ -28,7 +28,6 @@ import java.awt.*; import java.lang.ref.WeakReference; import java.net.*; import javax.swing.*; -import sun.awt.AppContext; import sun.swing.plaf.synth.Paint9Painter; /** @@ -41,8 +40,6 @@ import sun.swing.plaf.synth.Paint9Painter; * @author Scott Violet */ class ImagePainter extends SynthPainter { - private static final StringBuffer CACHE_KEY = - new StringBuffer("SynthCacheKey"); private Image image; private Insets sInsets; @@ -53,22 +50,17 @@ class ImagePainter extends SynthPainter { private Paint9Painter imageCache; private boolean center; + private static volatile WeakReference cacheRef; + private static Paint9Painter getPaint9Painter() { // A SynthPainter is created per . We want the - // cache to be shared by all, and we don't use a static because we - // don't want it to persist between look and feels. For that reason - // we use a AppContext specific Paint9Painter. It's backed via + // cache to be shared by all. It's held via // a WeakRef so that it can go away if the look and feel changes. - synchronized(CACHE_KEY) { - @SuppressWarnings("unchecked") - WeakReference cacheRef = - (WeakReference)AppContext.getAppContext(). - get(CACHE_KEY); + synchronized(ImagePainter.class) { Paint9Painter painter; if (cacheRef == null || (painter = cacheRef.get()) == null) { painter = new Paint9Painter(30); cacheRef = new WeakReference(painter); - AppContext.getAppContext().put(CACHE_KEY, cacheRef); } return painter; } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/Region.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/Region.java index 5a59f3bb98d..f8a0800ff8b 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/Region.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/Region.java @@ -24,8 +24,6 @@ */ package javax.swing.plaf.synth; -import sun.awt.AppContext; - import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -72,8 +70,6 @@ import javax.swing.UIDefaults; * @author Scott Violet */ public class Region { - private static final Object UI_TO_REGION_MAP_KEY = new Object(); - private static final Object LOWER_CASE_NAME_MAP_KEY = new Object(); /** * ArrowButton's are special types of buttons that also render a @@ -425,10 +421,10 @@ public class Region { */ public static final Region VIEWPORT = new Region("Viewport", false); - private static Map getUItoRegionMap() { - AppContext context = AppContext.getAppContext(); - @SuppressWarnings("unchecked") - Map map = (Map) context.get(UI_TO_REGION_MAP_KEY); + private static Map regionMap; + + private static synchronized Map getUItoRegionMap() { + Map map = regionMap; if (map == null) { map = new HashMap(); map.put("ArrowButtonUI", ARROW_BUTTON); @@ -476,18 +472,18 @@ public class Region { map.put("ToolBarSeparatorUI", TOOL_BAR_SEPARATOR); map.put("TreeUI", TREE); map.put("ViewportUI", VIEWPORT); - context.put(UI_TO_REGION_MAP_KEY, map); + regionMap = map; } return map; } - private static Map getLowerCaseNameMap() { - AppContext context = AppContext.getAppContext(); - @SuppressWarnings("unchecked") - Map map = (Map) context.get(LOWER_CASE_NAME_MAP_KEY); + private static Map lcRegionMap; + + private static synchronized Map getLowerCaseNameMap() { + Map map = lcRegionMap; if (map == null) { map = new HashMap(); - context.put(LOWER_CASE_NAME_MAP_KEY, map); + lcRegionMap = map; } return map; } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthButtonUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthButtonUI.java index 3a3d5719605..f0c7a9a1622 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthButtonUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthButtonUI.java @@ -25,8 +25,6 @@ package javax.swing.plaf.synth; -import sun.awt.AppContext; - import javax.swing.*; import java.awt.*; import java.beans.*; 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 700e3ed8a38..095d675d3de 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 @@ -60,7 +60,6 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.InsetsUIResource; import javax.swing.plaf.basic.BasicLookAndFeel; -import sun.awt.AppContext; import sun.awt.SunToolkit; import sun.swing.DefaultLookup; import sun.swing.SwingAccessor; @@ -101,31 +100,13 @@ public class SynthLookAndFeel extends BasicLookAndFeel { static final Insets EMPTY_UIRESOURCE_INSETS = new InsetsUIResource( 0, 0, 0, 0); - /** - * AppContext key to get the current SynthStyleFactory. - */ - private static final Object STYLE_FACTORY_KEY = - new StringBuffer("com.sun.java.swing.plaf.gtk.StyleCache"); + private static ComponentUI selectedUI; + private static int selectedUIStateValue; /** - * AppContext key to get selectedUI. - */ - private static final Object SELECTED_UI_KEY = new StringBuilder("selectedUI"); - - /** - * AppContext key to get selectedUIState. - */ - private static final Object SELECTED_UI_STATE_KEY = new StringBuilder("selectedUIState"); - - /** - * The last SynthStyleFactory that was asked for from AppContext - * lastContext. + * The last SynthStyleFactory that was set. */ private static SynthStyleFactory lastFactory; - /** - * AppContext lastLAF came from. - */ - private static AppContext lastContext; /** * SynthStyleFactory for the this SynthLookAndFeel. @@ -141,7 +122,7 @@ public class SynthLookAndFeel extends BasicLookAndFeel { private Handler _handler; static ComponentUI getSelectedUI() { - return (ComponentUI) AppContext.getAppContext().get(SELECTED_UI_KEY); + return selectedUI; } /** @@ -182,23 +163,20 @@ public class SynthLookAndFeel extends BasicLookAndFeel { } } - AppContext context = AppContext.getAppContext(); - - context.put(SELECTED_UI_KEY, uix); - context.put(SELECTED_UI_STATE_KEY, Integer.valueOf(selectedUIState)); + selectedUI = uix; + selectedUIStateValue = selectedUIState; } static int getSelectedUIState() { - Integer result = (Integer) AppContext.getAppContext().get(SELECTED_UI_STATE_KEY); - - return result == null ? 0 : result.intValue(); + return selectedUIStateValue; } /** * Clears out the selected UI that was last set in setSelectedUI. */ static void resetSelectedUI() { - AppContext.getAppContext().remove(SELECTED_UI_KEY); + selectedUI = null; + selectedUIStateValue = 0; } @@ -210,12 +188,8 @@ public class SynthLookAndFeel extends BasicLookAndFeel { */ public static void setStyleFactory(SynthStyleFactory cache) { // We assume the setter is called BEFORE the getter has been invoked - // for a particular AppContext. synchronized(SynthLookAndFeel.class) { - AppContext context = AppContext.getAppContext(); lastFactory = cache; - lastContext = context; - context.put(STYLE_FACTORY_KEY, cache); } } @@ -226,13 +200,6 @@ public class SynthLookAndFeel extends BasicLookAndFeel { */ public static SynthStyleFactory getStyleFactory() { synchronized(SynthLookAndFeel.class) { - AppContext context = AppContext.getAppContext(); - - if (lastContext == context) { - return lastFactory; - } - lastContext = context; - lastFactory = (SynthStyleFactory) context.get(STYLE_FACTORY_KEY); return lastFactory; } } diff --git a/src/java.desktop/share/classes/sun/awt/image/ImageFetcher.java b/src/java.desktop/share/classes/sun/awt/image/ImageFetcher.java index 74c7c4d2ce2..917378389a4 100644 --- a/src/java.desktop/share/classes/sun/awt/image/ImageFetcher.java +++ b/src/java.desktop/share/classes/sun/awt/image/ImageFetcher.java @@ -26,7 +26,6 @@ package sun.awt.image; import java.util.Vector; -import sun.awt.AppContext; /** * An ImageFetcher is a thread used to fetch ImageFetchable objects. @@ -34,10 +33,6 @@ import sun.awt.AppContext; * thread may also be used to animate it if necessary, via the * startingAnimation() / stoppingAnimation() methods. * - * There can be up to FetcherInfo.MAX_NUM_FETCHERS_PER_APPCONTEXT - * ImageFetcher threads for each AppContext. A per-AppContext queue - * of ImageFetchables is used to track objects to fetch. - * * @author Jim Graham * @author Fred Ecks */ @@ -153,7 +148,6 @@ class ImageFetcher extends Thread { info.numWaiting++; info.waitList.wait(end - now); } catch (InterruptedException e) { - // A normal occurrence as an AppContext is disposed return null; } finally { info.numWaiting--; @@ -280,28 +274,20 @@ class ImageFetcher extends Thread { // We need to instantiate a new ImageFetcher thread. // First, figure out which ThreadGroup we'll put the // new ImageFetcher into - final AppContext appContext = AppContext.getAppContext(); - ThreadGroup threadGroup = appContext.getThreadGroup(); - ThreadGroup fetcherThreadGroup; - if (threadGroup.getParent() != null) { - // threadGroup is not the root, so we proceed - fetcherThreadGroup = threadGroup; - } else { - // threadGroup is the root ("system") ThreadGroup. - // We instead want to use its child: the "main" - // ThreadGroup. Thus, we start with the current - // ThreadGroup, and go up the tree until - // threadGroup.getParent().getParent() == null. - threadGroup = Thread.currentThread().getThreadGroup(); - ThreadGroup parent = threadGroup.getParent(); - while ((parent != null) - && (parent.getParent() != null)) { - threadGroup = parent; - parent = threadGroup.getParent(); - } - fetcherThreadGroup = threadGroup; + // We don't want the root ("system") ThreadGroup. + // We instead want to use its child: the "main" + // ThreadGroup. Thus, we start with the current + // ThreadGroup, and go up the tree until + // threadGroup.getParent().getParent() == null. + ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); + ThreadGroup parent = threadGroup.getParent(); + while ((parent != null) + && (parent.getParent() != null)) { + threadGroup = parent; + parent = threadGroup.getParent(); } - final ThreadGroup fetcherGroup = fetcherThreadGroup; + final ThreadGroup fetcherGroup = threadGroup; + for (int i = 0; i < info.fetchers.length; i++) { if (info.fetchers[i] == null) { @@ -320,12 +306,13 @@ class ImageFetcher extends Thread { } /** - * The FetcherInfo class encapsulates the per-AppContext ImageFetcher + * The FetcherInfo class encapsulates the ImageFetcher * information. This includes the array of ImageFetchers, as well as * the queue of ImageFetchable objects. */ class FetcherInfo { - static final int MAX_NUM_FETCHERS_PER_APPCONTEXT = 4; + static final int MAX_NUM_FETCHERS = 4; + static final FetcherInfo FETCHER_INFO = new FetcherInfo(); Thread[] fetchers; int numFetchers; @@ -333,25 +320,13 @@ class FetcherInfo { Vector waitList; private FetcherInfo() { - fetchers = new Thread[MAX_NUM_FETCHERS_PER_APPCONTEXT]; + fetchers = new Thread[MAX_NUM_FETCHERS]; numFetchers = 0; numWaiting = 0; waitList = new Vector<>(); } - /* The key to put()/get() the FetcherInfo into/from the AppContext. */ - private static final Object FETCHER_INFO_KEY = - new StringBuffer("FetcherInfo"); - static FetcherInfo getFetcherInfo() { - AppContext appContext = AppContext.getAppContext(); - synchronized(appContext) { - FetcherInfo info = (FetcherInfo)appContext.get(FETCHER_INFO_KEY); - if (info == null) { - info = new FetcherInfo(); - appContext.put(FETCHER_INFO_KEY, info); - } - return info; - } + return FETCHER_INFO; } } diff --git a/src/java.desktop/share/classes/sun/swing/DefaultLookup.java b/src/java.desktop/share/classes/sun/swing/DefaultLookup.java index d7a7f22eba3..a2ae0921507 100644 --- a/src/java.desktop/share/classes/sun/swing/DefaultLookup.java +++ b/src/java.desktop/share/classes/sun/swing/DefaultLookup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, 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,7 +29,6 @@ import java.awt.Insets; import javax.swing.*; import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; -import sun.awt.AppContext; /** * DefaultLookup provides a way to customize the lookup done by the @@ -44,17 +43,9 @@ import sun.awt.AppContext; * @author Scott Violet */ public class DefaultLookup { + /** - * Key used to store DefaultLookup for AppContext. - */ - private static final Object DEFAULT_LOOKUP_KEY = new - StringBuffer("DefaultLookup"); - /** - * Thread that last asked for a default. - */ - private static Thread currentDefaultThread; - /** - * DefaultLookup for last thread. + * DefaultLookup currently set. */ private static DefaultLookup currentDefaultLookup; @@ -63,28 +54,24 @@ public class DefaultLookup { */ private static boolean isLookupSet; - /** - * Sets the DefaultLookup instance to use for the current - * AppContext. Null implies the UIManager should be - * used. + * Sets the DefaultLookup instance to use. + * Null implies the UIManager should be used. */ public static void setDefaultLookup(DefaultLookup lookup) { synchronized(DefaultLookup.class) { if (!isLookupSet && lookup == null) { - // Null was passed in, and no one has invoked setDefaultLookup + // Null was passed in, and no one has previously invoked setDefaultLookup // with a non-null value, we don't need to do anything. return; } else if (lookup == null) { - // null was passed in, but someone has invoked setDefaultLookup + // null was passed in, but someone has previously invoked setDefaultLookup // with a non-null value, use an instance of DefaultLookup // which will fallback to UIManager. lookup = new DefaultLookup(); } isLookupSet = true; - AppContext.getAppContext().put(DEFAULT_LOOKUP_KEY, lookup); - currentDefaultThread = Thread.currentThread(); currentDefaultLookup = lookup; } } @@ -98,27 +85,13 @@ public class DefaultLookup { // No one has set a valid DefaultLookup, use UIManager. return UIManager.get(key, c.getLocale()); } - Thread thisThread = Thread.currentThread(); DefaultLookup lookup; synchronized(DefaultLookup.class) { - // See if we've already cached the DefaultLookup for this thread, - // and use it if we have. - if (thisThread == currentDefaultThread) { - // It is cached, use it. - lookup = currentDefaultLookup; - } - else { - // Not cached, get the DefaultLookup to use from the AppContext - lookup = (DefaultLookup)AppContext.getAppContext().get( - DEFAULT_LOOKUP_KEY); - if (lookup == null) { - // Fallback to DefaultLookup, which will redirect to the - // UIManager. - lookup = new DefaultLookup(); - AppContext.getAppContext().put(DEFAULT_LOOKUP_KEY, lookup); - } - // Cache the values to make the next lookup easier. - currentDefaultThread = thisThread; + lookup = currentDefaultLookup; + if (lookup == null) { + // Fallback to DefaultLookup, which will redirect to the + // UIManager. + lookup = new DefaultLookup(); currentDefaultLookup = lookup; } } diff --git a/src/java.desktop/share/classes/sun/swing/SwingAccessor.java b/src/java.desktop/share/classes/sun/swing/SwingAccessor.java index 5c561de4a42..563cc038ae9 100644 --- a/src/java.desktop/share/classes/sun/swing/SwingAccessor.java +++ b/src/java.desktop/share/classes/sun/swing/SwingAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2026, 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 @@ -133,6 +133,33 @@ public final class SwingAccessor { KeyStroke create(); } + /** + * An accessor for the LAFState class state. + */ + public interface LAFStateAccessor { + boolean lafStateIsInitialized(); + } + + private static LAFStateAccessor lafStateAccessor; + /** + * Set an accessor object for the LAFState class. + */ + public static void setLAFStateAccessor(LAFStateAccessor accessor) { + lafStateAccessor = accessor; + } + + /** + * Retrieve the accessor object for the LAFState class. + */ + public static LAFStateAccessor getLAFStateAccessor() { + var access = lafStateAccessor; + if (access == null) { + ensureClassInitialized(UIManager.class); + access = lafStateAccessor; + } + return access; + } + /** * The javax.swing.JComponent class accessor object. */ diff --git a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java index 20a07c608a0..faf31e0b426 100644 --- a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java +++ b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2026, 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 @@ -120,12 +120,6 @@ import static java.awt.geom.AffineTransform.TYPE_TRANSLATION; * */ public class SwingUtilities2 { - /** - * The {@code AppContext} key for our one {@code LAFState} - * instance. - */ - public static final Object LAF_STATE_KEY = - new StringBuffer("LookAndFeel State"); public static final Object MENU_SELECTION_MANAGER_LISTENER_KEY = new StringBuffer("MenuSelectionManager listener key"); diff --git a/src/java.desktop/share/legal/harfbuzz.md b/src/java.desktop/share/legal/harfbuzz.md index e0c40705ed6..3aa5ca841d6 100644 --- a/src/java.desktop/share/legal/harfbuzz.md +++ b/src/java.desktop/share/legal/harfbuzz.md @@ -1,4 +1,4 @@ -## Harfbuzz 11.2.0 +## Harfbuzz 12.3.2 ### Harfbuzz License diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh index 73e6b4c305b..011b5ad5520 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh @@ -104,7 +104,7 @@ public: foreground (foreground_), instancer (instancer_) { - if (font->is_synthetic ()) + if (font->is_synthetic) { font = hb_font_create_sub_font (font); hb_font_set_synthetic_bold (font, 0, 0, true); @@ -178,7 +178,10 @@ struct hb_colrv1_closure_context_t : { glyphs->add (glyph_id); } void add_layer_indices (unsigned first_layer_index, unsigned num_of_layers) - { layer_indices->add_range (first_layer_index, first_layer_index + num_of_layers - 1); } + { + if (num_of_layers == 0) return; + layer_indices->add_range (first_layer_index, first_layer_index + num_of_layers - 1); + } void add_palette_index (unsigned palette_index) { palette_indices->add (palette_index); } @@ -650,10 +653,10 @@ 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), - HB_SERIALIZE_ERROR_INT_OVERFLOW)); - return_trace (true); + uint32_t first_layer_index = numLayers ? c->plan->colrv1_layers.get (firstLayerIndex) : 0; + return_trace (c->serializer->check_assign (out->firstLayerIndex, first_layer_index, + HB_SERIALIZE_ERROR_INT_OVERFLOW)); } bool sanitize (hb_sanitize_context_t *c) const @@ -1075,9 +1078,9 @@ struct PaintTranslate 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->funcs->push_translate (c->data, ddx, ddy); c->recurse (this+src); - if (p1) c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 14(noVar) or 15 (Var) */ @@ -1124,9 +1127,9 @@ struct PaintScale 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->funcs->push_scale (c->data, sx, sy); c->recurse (this+src); - if (p1) c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 16 (noVar) or 17(Var) */ @@ -1177,13 +1180,9 @@ struct PaintScaleAroundCenter 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->funcs->push_scale_around_center (c->data, sx, sy, 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); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 18 (noVar) or 19(Var) */ @@ -1228,9 +1227,9 @@ struct PaintScaleUniform TRACE_PAINT (this); float s = scale.to_float (c->instancer (varIdxBase, 0)); - bool p1 = c->funcs->push_scale (c->data, s, s); + c->funcs->push_scale (c->data, s, s); c->recurse (this+src); - if (p1) c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 20 (noVar) or 21(Var) */ @@ -1278,13 +1277,9 @@ struct PaintScaleUniformAroundCenter 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->funcs->push_scale_around_center (c->data, s, s, 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); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 22 (noVar) or 23(Var) */ @@ -1328,9 +1323,9 @@ struct PaintRotate TRACE_PAINT (this); float a = angle.to_float (c->instancer (varIdxBase, 0)); - bool p1 = c->funcs->push_rotate (c->data, a); + c->funcs->push_rotate (c->data, a); c->recurse (this+src); - if (p1) c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 24 (noVar) or 25(Var) */ @@ -1378,13 +1373,9 @@ struct PaintRotateAroundCenter 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->funcs->push_rotate_around_center (c->data, a, 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); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 26 (noVar) or 27(Var) */ @@ -1432,9 +1423,9 @@ struct PaintSkew 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->funcs->push_skew (c->data, sx, sy); c->recurse (this+src); - if (p1) c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 28(noVar) or 29 (Var) */ @@ -1485,13 +1476,9 @@ struct PaintSkewAroundCenter 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->funcs->push_skew_around_center (c->data, sx, sy, 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); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 30(noVar) or 31 (Var) */ @@ -1626,7 +1613,7 @@ struct ClipBox const ItemVarStoreInstancer &instancer) const { TRACE_SUBSET (this); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (u.format1.subset (c, instancer, VarIdx::NO_VARIATION)); case 2: return_trace (u.format2.subset (c, instancer)); default:return_trace (c->default_return_value ()); @@ -1635,7 +1622,7 @@ struct ClipBox void closurev1 (hb_colrv1_closure_context_t* c) const { - switch (u.format) { + switch (u.format.v) { case 2: u.format2.closurev1 (c); return; default:return; } @@ -1644,9 +1631,9 @@ 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); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); default:return_trace (c->default_return_value ()); @@ -1657,7 +1644,7 @@ struct ClipBox const ItemVarStoreInstancer &instancer) const { ClipBoxData clip_box; - switch (u.format) { + switch (u.format.v) { case 1: u.format1.get_clip_box (clip_box, instancer); break; @@ -1677,7 +1664,7 @@ struct ClipBox protected: union { - HBUINT8 format; /* Format identifier */ + struct { HBUINT8 v; } format; /* Format identifier */ ClipBoxFormat1 format1; ClipBoxFormat2 format2; } u; @@ -1857,9 +1844,9 @@ 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); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.paintformat1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.paintformat2, std::forward (ds)...)); case 3: return_trace (c->dispatch (u.paintformat3, std::forward (ds)...)); @@ -1898,7 +1885,7 @@ struct Paint protected: union { - HBUINT8 format; + struct { HBUINT8 v; } format; PaintColrLayers paintformat1; NoVariable paintformat2; Variable paintformat3; @@ -2073,7 +2060,7 @@ struct delta_set_index_map_subset_plan_t outer_bit_count = 1; inner_bit_count = 1; - if (unlikely (!output_map.resize (map_count, false))) return false; + if (unlikely (!output_map.resize_dirty (map_count))) return false; for (unsigned idx = 0; idx < map_count; idx++) { @@ -2693,7 +2680,8 @@ struct COLR { ItemVarStoreInstancer instancer (get_var_store_ptr (), get_delta_set_index_map_ptr (), - hb_array (font->coords, font->num_coords)); + hb_array (font->coords, + font->has_nonzero_coords ? font->num_coords : 0)); hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer); hb_decycler_node_t node (c.glyphs_decycler); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh index 51fc1b52af4..71417fdf3cf 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh @@ -307,6 +307,7 @@ struct CPAL if (first_color_to_layer_index.has (first_color_record_idx)) continue; first_color_index_for_layer.push (first_color_record_idx); + if (unlikely (!c->serializer->propagate_error (first_color_index_for_layer))) return_trace (false); first_color_to_layer_index.set (first_color_record_idx, first_color_index_for_layer.length - 1); } 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 index 35d73c7b858..28678856373 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh @@ -46,7 +46,7 @@ struct Coverage protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ CoverageFormat1_3 format1; CoverageFormat2_4 format2; #ifndef HB_NO_BEYOND_64K @@ -55,7 +55,7 @@ struct Coverage #endif } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); #ifndef HB_OPTIMIZE_SIZE HB_ALWAYS_INLINE @@ -63,9 +63,9 @@ struct Coverage bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) + switch (u.format.v) { case 1: return_trace (u.format1.sanitize (c)); case 2: return_trace (u.format2.sanitize (c)); @@ -86,7 +86,7 @@ struct Coverage 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) { + switch (u.format.v) { case 1: return u.format1.get_coverage (glyph_id); case 2: return u.format2.get_coverage (glyph_id); #ifndef HB_NO_BEYOND_64K @@ -97,18 +97,38 @@ struct Coverage } } unsigned int get_coverage (hb_codepoint_t glyph_id, - hb_ot_lookup_cache_t *cache) const + hb_ot_layout_mapping_cache_t *cache) const { unsigned coverage; - if (cache && cache->get (glyph_id, &coverage)) return coverage; + if (cache && cache->get (glyph_id, &coverage)) return coverage < cache->MAX_VALUE ? coverage : NOT_COVERED; coverage = get_coverage (glyph_id); - if (cache) cache->set (glyph_id, coverage); + if (cache) { + if (coverage == NOT_COVERED) + cache->set_unchecked (glyph_id, cache->MAX_VALUE); + else if (likely (coverage < cache->MAX_VALUE)) + cache->set_unchecked (glyph_id, coverage); + } + return coverage; + } + + unsigned int get_coverage_binary (hb_codepoint_t glyph_id, + hb_ot_layout_binary_cache_t *cache) const + { + unsigned coverage; + if (cache && cache->get (glyph_id, &coverage)) return coverage < cache->MAX_VALUE ? coverage : NOT_COVERED; + coverage = get_coverage (glyph_id); + if (cache) { + if (coverage == NOT_COVERED) + cache->set_unchecked (glyph_id, cache->MAX_VALUE); + else + cache->set_unchecked (glyph_id, 0); + } return coverage; } unsigned get_population () const { - switch (u.format) { + switch (u.format.v) { case 1: return u.format1.get_population (); case 2: return u.format2.get_population (); #ifndef HB_NO_BEYOND_64K @@ -140,11 +160,11 @@ struct Coverage last = g; if (g > max) max = g; } - u.format = !unsorted && count <= num_ranges * 3 ? 1 : 2; + u.format.v = !unsorted && count <= num_ranges * 3 ? 1 : 2; #ifndef HB_NO_BEYOND_64K if (max > 0xFFFFu) - u.format += 2; + u.format.v += 2; if (unlikely (max > 0xFFFFFFu)) #else if (unlikely (max > 0xFFFFu)) @@ -154,7 +174,7 @@ struct Coverage return_trace (false); } - switch (u.format) + switch (u.format.v) { case 1: return_trace (u.format1.serialize (c, glyphs)); case 2: return_trace (u.format2.serialize (c, glyphs)); @@ -185,7 +205,7 @@ struct Coverage bool intersects (const hb_set_t *glyphs) const { - switch (u.format) + switch (u.format.v) { case 1: return u.format1.intersects (glyphs); case 2: return u.format2.intersects (glyphs); @@ -198,7 +218,7 @@ struct Coverage } bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const { - switch (u.format) + switch (u.format.v) { case 1: return u.format1.intersects_coverage (glyphs, index); case 2: return u.format2.intersects_coverage (glyphs, index); @@ -212,7 +232,7 @@ struct Coverage unsigned cost () const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.cost (); case 2: hb_barrier (); return u.format2.cost (); #ifndef HB_NO_BEYOND_64K @@ -228,7 +248,7 @@ struct Coverage template bool collect_coverage (set_t *glyphs) const { - switch (u.format) + switch (u.format.v) { case 1: return u.format1.collect_coverage (glyphs); case 2: return u.format2.collect_coverage (glyphs); @@ -244,7 +264,7 @@ struct Coverage hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))> void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const { - switch (u.format) + switch (u.format.v) { case 1: return u.format1.intersect_set (glyphs, intersect_glyphs); case 2: return u.format2.intersect_set (glyphs, intersect_glyphs); @@ -262,7 +282,7 @@ struct Coverage iter_t (const Coverage &c_ = Null (Coverage)) { hb_memset (this, 0, sizeof (*this)); - format = c_.u.format; + format = c_.u.format.v; switch (format) { case 1: u.format1.init (c_.u.format1); return; @@ -332,7 +352,7 @@ struct Coverage } iter_t __end__ () const { - iter_t it = {}; + iter_t it; it.format = format; switch (format) { 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 index 97ea51089ec..122326e3024 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh @@ -41,11 +41,11 @@ struct CoverageFormat1_3 { friend struct Coverage; - protected: + public: HBUINT16 coverageFormat; /* Format identifier--format = 1 */ SortedArray16Of glyphArray; /* Array of GlyphIDs--in numerical order */ - public: + DEFINE_SIZE_ARRAY (4, glyphArray); private: 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 index 0c9bd09cbf7..8287c8d4d4d 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh @@ -40,7 +40,7 @@ struct CoverageFormat2_4 { friend struct Coverage; - protected: + public: HBUINT16 coverageFormat; /* Format identifier--format = 2 */ SortedArray16Of> rangeRecord; /* Array of glyph ranges--ordered by 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 index ceba37b0619..89e110990bc 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh @@ -252,7 +252,7 @@ struct CaretValue hb_codepoint_t glyph_id, const ItemVariationStore &var_store) const { - switch (u.format) { + switch (u.format.v) { 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); @@ -263,9 +263,9 @@ struct CaretValue 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) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { 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)...)); @@ -275,7 +275,7 @@ struct CaretValue void collect_variation_indices (hb_collect_variation_indices_context_t *c) const { - switch (u.format) { + switch (u.format.v) { case 1: case 2: return; @@ -289,9 +289,9 @@ struct CaretValue bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (u.format1.sanitize (c)); case 2: return_trace (u.format2.sanitize (c)); case 3: return_trace (u.format3.sanitize (c)); @@ -301,13 +301,13 @@ struct CaretValue protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ CaretValueFormat1 format1; CaretValueFormat2 format2; CaretValueFormat3 format3; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; struct LigGlyph @@ -519,7 +519,7 @@ struct MarkGlyphSets { bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const { - switch (u.format) { + switch (u.format.v) { case 1: return u.format1.covers (set_index, glyph_id); default:return false; } @@ -528,7 +528,7 @@ struct MarkGlyphSets template void collect_coverage (hb_vector_t &sets) const { - switch (u.format) { + switch (u.format.v) { case 1: u.format1.collect_coverage (sets); return; default:return; } @@ -537,7 +537,7 @@ struct MarkGlyphSets void collect_used_mark_sets (const hb_set_t& glyph_set, hb_set_t& used_mark_sets /* OUT */) const { - switch (u.format) { + switch (u.format.v) { case 1: u.format1.collect_used_mark_sets (glyph_set, used_mark_sets); return; default:return; } @@ -546,7 +546,7 @@ struct MarkGlyphSets bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (u.format1.subset (c)); default:return_trace (false); } @@ -555,9 +555,9 @@ struct MarkGlyphSets bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (u.format1.sanitize (c)); default:return_trace (true); } @@ -565,11 +565,11 @@ struct MarkGlyphSets protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ MarkGlyphSetsFormat1 format1; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; @@ -977,7 +977,7 @@ struct GDEF } #ifndef HB_NO_GDEF_CACHE - table->get_mark_glyph_sets ().collect_coverage (mark_glyph_set_digests); + table->get_mark_glyph_sets ().collect_coverage (mark_glyph_sets); #endif } ~accelerator_t () { table.destroy (); } @@ -1002,18 +1002,34 @@ struct GDEF } + HB_ALWAYS_INLINE bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const { return #ifndef HB_NO_GDEF_CACHE - mark_glyph_set_digests[set_index].may_have (glyph_id) && + // We can access arrayZ directly because of sanitize_lookup_props() guarantee. + mark_glyph_sets.arrayZ[set_index].may_have (glyph_id) && #endif - table->mark_set_covers (set_index, glyph_id); + table->mark_set_covers (set_index, glyph_id) + ; + } + + unsigned sanitize_lookup_props (unsigned lookup_props) const + { +#ifndef HB_NO_GDEF_CACHE + if (lookup_props & LookupFlag::UseMarkFilteringSet && + (lookup_props >> 16) >= mark_glyph_sets.length) + { + // Invalid mark filtering set index; unset the flag. + lookup_props &= ~LookupFlag::UseMarkFilteringSet; + } +#endif + return lookup_props; } hb_blob_ptr_t table; #ifndef HB_NO_GDEF_CACHE - hb_vector_t mark_glyph_set_digests; + hb_vector_t mark_glyph_sets; mutable hb_cache_t<21, 3> glyph_props_cache; static_assert (sizeof (glyph_props_cache) == 512, ""); #endif 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 7802e397f4c..1938803fa7c 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 @@ -13,20 +13,20 @@ struct Anchor { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ AnchorFormat1 format1; AnchorFormat2 format2; AnchorFormat3 format3; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (u.format1.sanitize (c)); case 2: return_trace (u.format2.sanitize (c)); case 3: return_trace (u.format3.sanitize (c)); @@ -38,7 +38,7 @@ struct Anchor float *x, float *y) const { *x = *y = 0; - switch (u.format) { + switch (u.format.v) { case 1: u.format1.get_anchor (c, glyph_id, x, y); return; case 2: u.format2.get_anchor (c, glyph_id, x, y); return; case 3: u.format3.get_anchor (c, glyph_id, x, y); return; @@ -49,7 +49,7 @@ struct Anchor bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (bool (reinterpret_cast (u.format1.copy (c->serializer)))); case 2: if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) @@ -66,7 +66,7 @@ struct Anchor void collect_variation_indices (hb_collect_variation_indices_context_t *c) const { - switch (u.format) { + switch (u.format.v) { case 1: case 2: return; case 3: 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 61bd90310a5..c49705bea0e 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 @@ -37,12 +37,12 @@ struct AnchorFormat3 *x = font->em_fscale_x (xCoordinate); *y = font->em_fscale_y (yCoordinate); - if ((font->x_ppem || font->num_coords) && xDeviceTable.sanitize (&c->sanitizer, this)) + if ((font->x_ppem || font->has_nonzero_coords) && xDeviceTable.sanitize (&c->sanitizer, this)) { hb_barrier (); *x += (this+xDeviceTable).get_x_delta (font, c->var_store, c->var_store_cache); } - if ((font->y_ppem || font->num_coords) && yDeviceTable.sanitize (&c->sanitizer, this)) + if ((font->y_ppem || font->has_nonzero_coords) && yDeviceTable.sanitize (&c->sanitizer, this)) { hb_barrier (); *y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache); @@ -91,10 +91,13 @@ struct AnchorFormat3 } } - /* in case that all axes are pinned or no variations after instantiation, - * both var_idxes will be mapped to HB_OT_LAYOUT_NO_VARIATIONS_INDEX */ - if (x_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX && - y_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX) + + bool no_downgrade = (!xDeviceTable.is_null () && !(this+xDeviceTable).is_variation_device ()) || + x_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX || + y_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX || + (!yDeviceTable.is_null () && !(this+yDeviceTable).is_variation_device ()); + + if (!no_downgrade) return_trace (c->serializer->check_assign (out->format, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW)); if (!c->serializer->embed (xDeviceTable)) return_trace (false); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorMatrix.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorMatrix.hh index 9da9fff50ba..128ced6c176 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorMatrix.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorMatrix.hh @@ -77,6 +77,13 @@ struct AnchorMatrix return_trace (true); } + + bool offset_is_null (unsigned row, unsigned col, unsigned num_cols) const + { + if (unlikely (row >= rows || col >= num_cols)) return true; + auto &offset = matrixZ[row * num_cols + col]; + return offset.is_null (); + } }; 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 0105a9b8542..38a29dd9ed5 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 @@ -11,7 +11,7 @@ struct CursivePos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ CursivePosFormat1 format1; } u; @@ -19,9 +19,9 @@ 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); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { 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 361aaed658a..f5a09e07d73 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 @@ -50,8 +50,9 @@ struct EntryExitRecord DEFINE_SIZE_STATIC (4); }; -static void -reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent) { +static inline void +reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent) +{ int chain = pos[i].attach_chain(), type = pos[i].attach_type(); if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE))) return; @@ -130,7 +131,7 @@ struct CursivePosFormat1 unlikely (!this_record.entryAnchor.sanitize (&c->sanitizer, this))) return_trace (false); hb_barrier (); - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_input; skippy_iter.reset_fast (buffer->idx); unsigned unsafe_from; if (unlikely (!skippy_iter.prev (&unsafe_from))) @@ -229,8 +230,13 @@ struct CursivePosFormat1 */ reverse_cursive_minor_offset (pos, child, c->direction, parent); - pos[child].attach_type() = ATTACH_TYPE_CURSIVE; pos[child].attach_chain() = (int) parent - (int) child; + if (pos[child].attach_chain() != (int) parent - (int) child) + { + pos[child].attach_chain() = 0; + goto overflow; + } + pos[child].attach_type() = ATTACH_TYPE_CURSIVE; buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) pos[child].y_offset = y_offset; @@ -256,6 +262,7 @@ struct CursivePosFormat1 i, j); } + overflow: buffer->idx++; return_trace (true); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh index ce3f74d8c3b..b80f606f7b5 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh @@ -80,9 +80,8 @@ propagate_attachment_offsets (hb_glyph_position_t *pos, { /* Adjusts offsets of attached glyphs (both cursive and mark) to accumulate * offset of glyph they are attached to. */ - int chain = pos[i].attach_chain(), type = pos[i].attach_type(); - if (likely (!chain)) - return; + int chain = pos[i].attach_chain(); + int type = pos[i].attach_type(); pos[i].attach_chain() = 0; @@ -94,7 +93,8 @@ propagate_attachment_offsets (hb_glyph_position_t *pos, if (unlikely (!nesting_level)) return; - propagate_attachment_offsets (pos, len, j, direction, nesting_level - 1); + if (pos[j].attach_chain()) + propagate_attachment_offsets (pos, len, j, direction, nesting_level - 1); assert (!!(type & GPOS_impl::ATTACH_TYPE_MARK) ^ !!(type & GPOS_impl::ATTACH_TYPE_CURSIVE)); @@ -110,17 +110,37 @@ propagate_attachment_offsets (hb_glyph_position_t *pos, pos[i].x_offset += pos[j].x_offset; pos[i].y_offset += pos[j].y_offset; - assert (j < i); - if (HB_DIRECTION_IS_FORWARD (direction)) - for (unsigned int k = j; k < i; k++) { - pos[i].x_offset -= pos[k].x_advance; - pos[i].y_offset -= pos[k].y_advance; - } - else - for (unsigned int k = j + 1; k < i + 1; k++) { - pos[i].x_offset += pos[k].x_advance; - pos[i].y_offset += pos[k].y_advance; - } + // i is the position of the mark; j is the base. + if (j < i) + { + /* This is the common case: mark follows base. + * And currently the only way in OpenType. */ + if (HB_DIRECTION_IS_FORWARD (direction)) + for (unsigned int k = j; k < i; k++) { + pos[i].x_offset -= pos[k].x_advance; + pos[i].y_offset -= pos[k].y_advance; + } + else + for (unsigned int k = j + 1; k < i + 1; k++) { + pos[i].x_offset += pos[k].x_advance; + pos[i].y_offset += pos[k].y_advance; + } + } + else // j > i + { + /* This can happen with `kerx`: a mark attaching + * to a base after it in the logical order. */ + if (HB_DIRECTION_IS_FORWARD (direction)) + for (unsigned int k = i; k < j; k++) { + pos[i].x_offset += pos[k].x_advance; + pos[i].y_offset += pos[k].y_advance; + } + else + for (unsigned int k = i + 1; k < j + 1; k++) { + pos[i].x_offset -= pos[k].x_advance; + pos[i].y_offset -= pos[k].y_advance; + } + } } } @@ -149,8 +169,20 @@ GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) /* Handle attachments */ if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT) - for (unsigned i = 0; i < len; i++) - propagate_attachment_offsets (pos, len, i, direction); + { + auto *pos = buffer->pos; + // https://github.com/harfbuzz/harfbuzz/issues/5514 + if (HB_DIRECTION_IS_FORWARD (direction)) + { + for (unsigned i = 0; i < len; i++) + if (pos[i].attach_chain()) + propagate_attachment_offsets (pos, len, i, direction); + } else { + for (unsigned i = len; i-- > 0; ) + if (pos[i].attach_chain()) + propagate_attachment_offsets (pos, len, i, direction); + } + } if (unlikely (font->slant_xy) && HB_DIRECTION_IS_HORIZONTAL (direction)) 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 index eecdb95a95f..113b693dc3e 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/LigatureArray.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/LigatureArray.hh @@ -19,22 +19,30 @@ struct LigatureArray : List16OfOffset16To bool subset (hb_subset_context_t *c, Iterator coverage, unsigned class_count, - const hb_map_t *klass_mapping) const + const hb_map_t *klass_mapping, + hb_sorted_vector_t &new_coverage /* OUT */) const { TRACE_SUBSET (this); - const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = c->plan->glyph_map_gsub; auto *out = c->serializer->start_embed (this); if (unlikely (!c->serializer->extend_min (out))) return_trace (false); bool ret = false; for (const auto _ : + hb_zip (coverage, *this) - | hb_filter (glyphset, hb_first)) + | hb_filter (glyph_map, hb_first)) { + const LigatureAttach& src = (this + _.second); + bool non_empty = + hb_range (src.rows * class_count) + | hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); }) + | hb_map ([&] (const unsigned index) { return !src.offset_is_null (index / class_count, index % class_count, class_count); }) + | hb_any; + + if (!non_empty) continue; + 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); }) @@ -44,6 +52,9 @@ struct LigatureArray : List16OfOffset16To this, src.rows, indexes); + + hb_codepoint_t new_gid = glyph_map.get (_.first); + new_coverage.push (new_gid); } return_trace (ret); } 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 abae8f1c607..bddc5e7fe9e 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 @@ -47,10 +47,15 @@ struct MarkArray : Array16Of /* Array of MarkRecords--in Cove } hb_glyph_position_t &o = buffer->cur_pos(); + o.attach_chain() = (int) glyph_pos - (int) buffer->idx; + if (o.attach_chain() != (int) glyph_pos - (int) buffer->idx) + { + o.attach_chain() = 0; + goto overflow; + } + o.attach_type() = ATTACH_TYPE_MARK; o.x_offset = roundf (base_x - mark_x); o.y_offset = roundf (base_y - mark_y); - o.attach_type() = ATTACH_TYPE_MARK; - 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 ()) @@ -60,6 +65,7 @@ struct MarkArray : Array16Of /* Array of MarkRecords--in Cove c->buffer->idx, glyph_pos); } + overflow: buffer->idx++; return_trace (true); } 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 b1d1118a86b..65f343bda8b 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,7 +11,7 @@ struct MarkBasePos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ MarkBasePosFormat1_2 format1; #ifndef HB_NO_BEYOND_64K MarkBasePosFormat1_2 format2; @@ -22,9 +22,9 @@ struct MarkBasePos 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) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { 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)...)); 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 e633f7a1be1..ad071f327ea 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 @@ -119,7 +119,7 @@ struct MarkBasePosFormat1_2 /* 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; + auto &skippy_iter = c->iter_input; skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); if (c->last_base_until > buffer->idx) @@ -209,19 +209,22 @@ struct MarkBasePosFormat1_2 ; new_coverage.reset (); - + base_iter - | hb_map (hb_first) - | hb_map (glyph_map) - | hb_sink (new_coverage) - ; - - if (!out->baseCoverage.serialize_serialize (c->serializer, new_coverage.iter ())) - return_trace (false); - hb_sorted_vector_t base_indexes; - for (const unsigned row : + base_iter - | hb_map (hb_second)) + auto &base_array = (this+baseArray); + for (const auto _ : + base_iter) { + unsigned row = _.second; + bool non_empty = + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return !base_array.offset_is_null (row, col, (unsigned) classCount); }) + | hb_any + ; + + if (!non_empty) continue; + + hb_codepoint_t new_g = glyph_map.get ( _.first); + new_coverage.push (new_g); + + hb_range ((unsigned) classCount) | hb_filter (klass_mapping) | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) @@ -229,8 +232,12 @@ struct MarkBasePosFormat1_2 ; } + if (!new_coverage) return_trace (false); + if (!out->baseCoverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + return_trace (out->baseArray.serialize_subset (c, baseArray, this, - base_iter.len (), + new_coverage.length, base_indexes.iter ())); } }; 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 b10102880c0..ee237eb479f 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,7 +11,7 @@ struct MarkLigPos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ MarkLigPosFormat1_2 format1; #ifndef HB_NO_BEYOND_64K MarkLigPosFormat1_2 format2; @@ -22,9 +22,9 @@ struct MarkLigPos 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) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { 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)...)); 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 cf4cbae9a3f..509a26c2485 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 @@ -101,7 +101,7 @@ struct MarkLigPosFormat1_2 /* Now we search backwards for a non-mark glyph */ - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_input; skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); if (c->last_base_until > buffer->idx) @@ -200,19 +200,13 @@ struct MarkLigPosFormat1_2 &klass_mapping))) return_trace (false); - auto new_ligature_coverage = - + hb_iter (this + ligatureCoverage) - | hb_take ((this + ligatureArray).len) - | hb_map_retains_sorting (glyph_map) - | hb_filter ([] (hb_codepoint_t glyph) { return glyph != HB_MAP_VALUE_INVALID; }) - ; - - if (!out->ligatureCoverage.serialize_serialize (c->serializer, new_ligature_coverage)) + hb_sorted_vector_t new_lig_coverage; + if (!out->ligatureArray.serialize_subset (c, ligatureArray, this, + hb_iter (this+ligatureCoverage), + classCount, &klass_mapping, new_lig_coverage)) return_trace (false); - return_trace (out->ligatureArray.serialize_subset (c, ligatureArray, this, - hb_iter (this+ligatureCoverage), - classCount, &klass_mapping)); + return_trace (out->ligatureCoverage.serialize_serialize (c->serializer, new_lig_coverage.iter ())); } }; 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 e0d9eca0280..c06f013cdce 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,7 +11,7 @@ struct MarkMarkPos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ MarkMarkPosFormat1_2 format1; #ifndef HB_NO_BEYOND_64K MarkMarkPosFormat1_2 format2; @@ -22,9 +22,9 @@ struct MarkMarkPos 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) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { 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)...)); 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 6519e79b443..c93dbbb3f06 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 @@ -100,7 +100,7 @@ struct MarkMarkPosFormat1_2 if (likely (mark1_index == NOT_COVERED)) return_trace (false); /* 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; + auto &skippy_iter = c->iter_input; skippy_iter.reset_fast (buffer->idx); skippy_iter.set_lookup_props (c->lookup_props & ~(uint32_t)LookupFlag::IgnoreFlags); unsigned unsafe_from; @@ -196,19 +196,23 @@ struct MarkMarkPosFormat1_2 ; new_coverage.reset (); - + mark2_iter - | hb_map (hb_first) - | hb_map (glyph_map) - | hb_sink (new_coverage) - ; - - if (!out->mark2Coverage.serialize_serialize (c->serializer, new_coverage.iter ())) - return_trace (false); - hb_sorted_vector_t mark2_indexes; - for (const unsigned row : + mark2_iter - | hb_map (hb_second)) + auto &mark2_array = (this+mark2Array); + for (const auto _ : + mark2_iter) { + unsigned row = _.second; + + bool non_empty = + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return !mark2_array.offset_is_null (row, col, (unsigned) classCount); }) + | hb_any + ; + + if (!non_empty) continue; + + hb_codepoint_t new_g = glyph_map.get ( _.first); + new_coverage.push (new_g); + + hb_range ((unsigned) classCount) | hb_filter (klass_mapping) | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) @@ -216,6 +220,10 @@ struct MarkMarkPosFormat1_2 ; } + if (!new_coverage) return_trace (false); + if (!out->mark2Coverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + return_trace (out->mark2Array.serialize_subset (c, mark2Array, this, mark2_iter.len (), mark2_indexes.iter ())); 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 e3794ea9ed5..bf7fff7face 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,7 +12,7 @@ struct PairPos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ PairPosFormat1_3 format1; PairPosFormat2_4 format2; #ifndef HB_NO_BEYOND_64K @@ -25,9 +25,9 @@ struct PairPos 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) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { 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 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 2748882f527..1c067bde86f 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 @@ -103,52 +103,35 @@ struct PairPosFormat1_3 const Coverage &get_coverage () const { return this+coverage; } - unsigned cache_cost () const + struct external_cache_t { - return (this+coverage).cost (); - } - static void * cache_func (void *p, hb_ot_lookup_cache_op_t op) + hb_ot_layout_mapping_cache_t coverage; + }; + void *external_cache_create () const { - switch (op) + external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t)); + if (likely (cache)) { - case hb_ot_lookup_cache_op_t::CREATE: - { - hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) hb_malloc (sizeof (hb_ot_lookup_cache_t)); - if (likely (cache)) - cache->clear (); - return cache; - } - case hb_ot_lookup_cache_op_t::ENTER: - return (void *) true; - case hb_ot_lookup_cache_op_t::LEAVE: - return nullptr; - case hb_ot_lookup_cache_op_t::DESTROY: - { - hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) p; - hb_free (cache); - return nullptr; - } + cache->coverage.clear (); } - return nullptr; + return cache; } - bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } - bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); } - bool _apply (hb_ot_apply_context_t *c, bool cached) const + bool apply (hb_ot_apply_context_t *c, void *external_cache) const { TRACE_APPLY (this); hb_buffer_t *buffer = c->buffer; #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - hb_ot_lookup_cache_t *cache = cached ? (hb_ot_lookup_cache_t *) c->lookup_accel->cache : nullptr; - unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache); + external_cache_t *cache = (external_cache_t *) external_cache; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache ? &cache->coverage : nullptr); #else unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); #endif if (index == NOT_COVERED) return_trace (false); - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_input; skippy_iter.reset_fast (buffer->idx); unsigned unsafe_to; if (unlikely (!skippy_iter.next (&unsafe_to))) 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 d85b1ac2c17..ce731450f41 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 @@ -123,63 +123,39 @@ struct PairPosFormat2_4 : ValueBase const Coverage &get_coverage () const { return this+coverage; } - struct pair_pos_cache_t + struct external_cache_t { - hb_ot_lookup_cache_t coverage; - hb_ot_lookup_cache_t first; - hb_ot_lookup_cache_t second; + hb_ot_layout_mapping_cache_t coverage; + hb_ot_layout_mapping_cache_t first; + hb_ot_layout_mapping_cache_t second; }; - - unsigned cache_cost () const + void *external_cache_create () const { - return (this+coverage).cost () + (this+classDef1).cost () + (this+classDef2).cost (); - } - static void * cache_func (void *p, hb_ot_lookup_cache_op_t op) - { - switch (op) + external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t)); + if (likely (cache)) { - case hb_ot_lookup_cache_op_t::CREATE: - { - pair_pos_cache_t *cache = (pair_pos_cache_t *) hb_malloc (sizeof (pair_pos_cache_t)); - if (likely (cache)) - { - cache->coverage.clear (); - cache->first.clear (); - cache->second.clear (); - } - return cache; - } - case hb_ot_lookup_cache_op_t::ENTER: - return (void *) true; - case hb_ot_lookup_cache_op_t::LEAVE: - return nullptr; - case hb_ot_lookup_cache_op_t::DESTROY: - { - pair_pos_cache_t *cache = (pair_pos_cache_t *) p; - hb_free (cache); - return nullptr; - } + cache->coverage.clear (); + cache->first.clear (); + cache->second.clear (); } - return nullptr; + return cache; } - bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } - bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); } - bool _apply (hb_ot_apply_context_t *c, bool cached) const + bool apply (hb_ot_apply_context_t *c, void *external_cache) const { TRACE_APPLY (this); hb_buffer_t *buffer = c->buffer; #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - pair_pos_cache_t *cache = cached ? (pair_pos_cache_t *) c->lookup_accel->cache : nullptr; + external_cache_t *cache = (external_cache_t *) external_cache; unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache ? &cache->coverage : nullptr); #else unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); #endif if (index == NOT_COVERED) return_trace (false); - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_input; skippy_iter.reset_fast (buffer->idx); unsigned unsafe_to; if (unlikely (!skippy_iter.next (&unsafe_to))) 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 a0243a218c5..30fc1aacda8 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 @@ -12,7 +12,7 @@ struct SinglePos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ SinglePosFormat1 format1; SinglePosFormat2 format2; } u; @@ -41,7 +41,7 @@ struct SinglePos const hb_hashmap_t> *layout_variation_idx_delta_map, unsigned newFormat) { - if (unlikely (!c->extend_min (u.format))) return; + if (unlikely (!c->extend_min (u.format.v))) return; unsigned format = 2; ValueFormat new_format; new_format = newFormat; @@ -49,8 +49,8 @@ struct SinglePos if (glyph_val_iter_pairs) format = get_format (glyph_val_iter_pairs); - u.format = format; - switch (u.format) { + u.format.v = format; + switch (u.format.v) { case 1: u.format1.serialize (c, src, glyph_val_iter_pairs, @@ -70,9 +70,9 @@ 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); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); default:return_trace (c->default_return_value ()); 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 731d1ffca1a..b961a5139d8 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 @@ -56,9 +56,14 @@ struct ValueFormat : HBUINT16 * PosTable (may be NULL) */ #endif - IntType& operator = (uint16_t i) { v = i; return *this; } + NumType& operator = (uint16_t i) { v = i; return *this; } - unsigned int get_len () const { return hb_popcount ((unsigned int) *this); } + // Note: spec says skip 2 bytes per bit in the valueformat. But reports + // from Microsoft developers indicate that only the fields that are + // currently defined are counted. We don't expect any new fields to + // be added to ValueFormat. As such, we use the faster hb_popcount8 + // that only processes the lowest 8 bits. + unsigned int get_len () const { return hb_popcount8 ((uint8_t) *this); } unsigned int get_size () const { return get_len () * Value::static_size; } hb_vector_t get_device_table_indices () const { @@ -111,8 +116,8 @@ struct ValueFormat : HBUINT16 if (!has_device ()) return ret; - bool use_x_device = font->x_ppem || font->num_coords; - bool use_y_device = font->y_ppem || font->num_coords; + bool use_x_device = font->x_ppem || font->has_nonzero_coords; + bool use_y_device = font->y_ppem || font->has_nonzero_coords; if (!use_x_device && !use_y_device) return ret; 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 b5d506f36f9..f13c5e7e251 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 @@ -91,6 +91,19 @@ struct AlternateSet return alternates.len; } + void + collect_alternates (hb_codepoint_t gid, + hb_map_t *alternate_count /* IN/OUT */, + hb_map_t *alternate_glyphs /* IN/OUT */) const + { + + hb_enumerate (alternates) + | hb_map ([gid] (hb_pair_t _) { return hb_pair (gid + (_.first << 24), _.second); }) + | hb_apply ([&] (const hb_pair_t &p) -> void + { _hb_collect_glyph_alternates_add (p.first, p.second, + alternate_count, alternate_glyphs); }) + ; + } + template bool serialize (hb_serialize_context_t *c, 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 8951f5a7a17..a43c75c8f4e 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 @@ -12,7 +12,7 @@ struct AlternateSubst { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ AlternateSubstFormat1_2 format1; #ifndef HB_NO_BEYOND_64K AlternateSubstFormat1_2 format2; @@ -23,9 +23,9 @@ struct AlternateSubst 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) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { 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)...)); @@ -42,10 +42,10 @@ struct AlternateSubst hb_array_t alternate_glyphs_list) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (u.format))) return_trace (false); + if (unlikely (!c->extend_min (u.format.v))) return_trace (false); unsigned int format = 1; - u.format = format; - switch (u.format) { + u.format.v = format; + switch (u.format.v) { case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list)); default:return_trace (false); } 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 421a6e06627..7a3a2511b7f 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 @@ -69,6 +69,19 @@ struct AlternateSubstFormat1_2 { return (this+alternateSet[(this+coverage).get_coverage (gid)]) .get_alternates (start_offset, alternate_count, alternate_glyphs); } + void + collect_glyph_alternates (hb_map_t *alternate_count /* IN/OUT */, + hb_map_t *alternate_glyphs /* IN/OUT */) const + { + + hb_iter (alternateSet) + | hb_map (hb_add (this)) + | hb_zip (this+coverage) + | hb_apply ([&] (const hb_pair_t &, hb_codepoint_t> _) { + _.first.collect_alternates (_.second, alternate_count, alternate_glyphs); + }) + ; + } + bool apply (hb_ot_apply_context_t *c) const { TRACE_APPLY (this); 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 726da458fac..7bc98d31f32 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 @@ -44,6 +44,18 @@ struct Ligature c->output->add (ligGlyph); } + template + void collect_second (set_t &s) const + { + if (unlikely (!component.get_length ())) + { + // A ligature without any components. Anything matches. + s = set_t::full (); + return; + } + s.add (component.arrayZ[0]); + } + bool would_apply (hb_would_apply_context_t *c) const { if (c->len != component.lenP1) @@ -91,15 +103,6 @@ struct Ligature unsigned int total_component_count = 0; if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return false; - unsigned match_positions_stack[4]; - unsigned *match_positions = match_positions_stack; - if (unlikely (count > ARRAY_LENGTH (match_positions_stack))) - { - match_positions = (unsigned *) hb_malloc (hb_max (count, 1u) * sizeof (unsigned)); - if (unlikely (!match_positions)) - return_trace (false); - } - unsigned int match_end = 0; if (likely (!match_input (c, count, @@ -107,12 +110,9 @@ struct Ligature match_glyph, nullptr, &match_end, - match_positions, &total_component_count))) { c->buffer->unsafe_to_concat (c->buffer->idx, match_end); - if (match_positions != match_positions_stack) - hb_free (match_positions); return_trace (false); } @@ -129,10 +129,10 @@ struct Ligature match_end += delta; for (unsigned i = 0; i < count; i++) { - match_positions[i] += delta; + c->match_positions[i] += delta; if (i) *p++ = ','; - snprintf (p, sizeof(buf) - (p - buf), "%u", match_positions[i]); + snprintf (p, sizeof(buf) - (p - buf), "%u", c->match_positions[i]); p += strlen(p); } @@ -143,7 +143,6 @@ struct Ligature ligate_input (c, count, - match_positions, match_end, ligGlyph, total_component_count); @@ -156,8 +155,6 @@ struct Ligature pos); } - if (match_positions != match_positions_stack) - hb_free (match_positions); 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 ff0ffce94d7..81c5c2bcfe4 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 @@ -11,11 +11,11 @@ namespace GSUB_impl { template struct LigatureSet { - protected: + public: Array16OfOffset16To> ligature; /* Array LigatureSet tables * ordered by preference */ - public: + DEFINE_SIZE_ARRAY (2, ligature); bool sanitize (hb_sanitize_context_t *c) const @@ -62,6 +62,15 @@ struct LigatureSet ; } + template + void collect_seconds (set_t &s) const + { + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_apply ([&s] (const Ligature &_) { _.collect_second (s); }) + ; + } + bool would_apply (hb_would_apply_context_t *c) const { return @@ -72,14 +81,14 @@ struct LigatureSet ; } - bool apply (hb_ot_apply_context_t *c) const + bool apply (hb_ot_apply_context_t *c, const hb_set_digest_t *seconds = nullptr) const { TRACE_APPLY (this); unsigned int num_ligs = ligature.len; #ifndef HB_NO_OT_RULESETS_FAST_PATH - if (HB_OPTIMIZE_SIZE_VAL || num_ligs <= 4) + if (HB_OPTIMIZE_SIZE_VAL || num_ligs <= 1) #endif { slow: @@ -91,21 +100,21 @@ struct LigatureSet return_trace (false); } - /* This version is optimized for speed by matching the first component + /* This version is optimized for speed by matching the second component * of the ligature here, instead of calling into the ligation code. * * This is replicated in ChainRuleSet and RuleSet. */ - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_context; skippy_iter.reset (c->buffer->idx); skippy_iter.set_match_func (match_always, nullptr); skippy_iter.set_glyph_data ((HBUINT16 *) nullptr); unsigned unsafe_to; - hb_codepoint_t first = (unsigned) -1; + hb_codepoint_t second = (unsigned) -1; bool matched = skippy_iter.next (&unsafe_to); if (likely (matched)) { - first = c->buffer->info[skippy_iter.idx].codepoint; + second = c->buffer->info[skippy_iter.idx].codepoint; unsafe_to = skippy_iter.idx + 1; if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx])) @@ -118,13 +127,14 @@ struct LigatureSet else goto slow; + if (seconds && !seconds->may_have (second)) + return_trace (false); bool unsafe_to_concat = false; - for (unsigned int i = 0; i < num_ligs; i++) { const auto &lig = this+ligature.arrayZ[i]; if (unlikely (lig.component.lenP1 <= 1) || - lig.component.arrayZ[0] == first) + lig.component.arrayZ[0] == second) { if (lig.apply (c)) { 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 cffa910295f..22ce9b79d7f 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 @@ -12,7 +12,7 @@ struct LigatureSubst { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ LigatureSubstFormat1_2 format1; #ifndef HB_NO_BEYOND_64K LigatureSubstFormat1_2 format2; @@ -23,9 +23,9 @@ struct LigatureSubst 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) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { 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)...)); @@ -45,10 +45,10 @@ struct LigatureSubst hb_array_t component_list /* Starting from second for each ligature */) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (u.format))) return_trace (false); + if (unlikely (!c->extend_min (u.format.v))) return_trace (false); unsigned int format = 1; - u.format = format; - switch (u.format) { + u.format.v = format; + switch (u.format.v) { case 1: return_trace (u.format1.serialize (c, first_glyphs, ligature_per_first_glyph_count_list, 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 6ae24b33754..909ddca220f 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 @@ -78,52 +78,44 @@ struct LigatureSubstFormat1_2 return lig_set.would_apply (c); } - unsigned cache_cost () const + struct external_cache_t { - return (this+coverage).cost (); - } - static void * cache_func (void *p, hb_ot_lookup_cache_op_t op) + hb_ot_layout_mapping_cache_t coverage; + hb_set_digest_t seconds; + }; + void *external_cache_create () const { - switch (op) + external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t)); + if (likely (cache)) { - case hb_ot_lookup_cache_op_t::CREATE: - { - hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) hb_malloc (sizeof (hb_ot_lookup_cache_t)); - if (likely (cache)) - cache->clear (); - return cache; - } - case hb_ot_lookup_cache_op_t::ENTER: - return (void *) true; - case hb_ot_lookup_cache_op_t::LEAVE: - return nullptr; - case hb_ot_lookup_cache_op_t::DESTROY: - { - hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) p; - hb_free (cache); - return nullptr; - } + cache->coverage.clear (); + + cache->seconds.init (); + + hb_iter (ligatureSet) + | hb_map (hb_add (this)) + | hb_apply ([cache] (const LigatureSet &_) { _.collect_seconds (cache->seconds); }) + ; } - return nullptr; + return cache; } - bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } - bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); } - bool _apply (hb_ot_apply_context_t *c, bool cached) const + bool apply (hb_ot_apply_context_t *c, void *external_cache) const { TRACE_APPLY (this); hb_buffer_t *buffer = c->buffer; #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - hb_ot_lookup_cache_t *cache = cached ? (hb_ot_lookup_cache_t *) c->lookup_accel->cache : nullptr; - unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache); + external_cache_t *cache = (external_cache_t *) external_cache; + const hb_set_digest_t *seconds = cache ? &cache->seconds : nullptr; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache ? &cache->coverage : nullptr); #else - unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + const hb_set_digest_t *seconds = nullptr; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); #endif if (index == NOT_COVERED) return_trace (false); const auto &lig_set = this+ligatureSet[index]; - return_trace (lig_set.apply (c)); + return_trace (lig_set.apply (c, seconds)); } bool serialize (hb_serialize_context_t *c, 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 cf3d754e3cc..8fb663825b5 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 @@ -12,7 +12,7 @@ struct MultipleSubst { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ MultipleSubstFormat1_2 format1; #ifndef HB_NO_BEYOND_64K MultipleSubstFormat1_2 format2; @@ -24,9 +24,9 @@ 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); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { 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)...)); @@ -41,10 +41,10 @@ struct MultipleSubst Iterator it) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (u.format))) return_trace (false); + if (unlikely (!c->extend_min (u.format.v))) return_trace (false); unsigned int format = 1; - u.format = format; - switch (u.format) { + u.format.v = format; + switch (u.format.v) { case 1: return_trace (u.format1.serialize (c, it)); default:return_trace (false); } 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 5ad463fea79..e33148d770b 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 @@ -12,7 +12,7 @@ struct ReverseChainSingleSubst { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ReverseChainSingleSubstFormat1 format1; } u; @@ -20,9 +20,9 @@ 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); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { 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/Sequence.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Sequence.hh index a5e93a98bef..b3295bee16d 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 @@ -115,7 +115,7 @@ struct Sequence for (unsigned i = c->buffer->idx - count; i < c->buffer->idx; i++) { - if (buf < p) + if (buf < p && sizeof(buf) - 1u > unsigned (p - buf)) *p++ = ','; snprintf (p, sizeof(buf) - (p - buf), "%u", i); p += strlen(p); 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 b84259e7f00..323eb4d0f90 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 @@ -13,7 +13,7 @@ struct SingleSubst { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ SingleSubstFormat1_3 format1; SingleSubstFormat2_4 format2; #ifndef HB_NO_BEYOND_64K @@ -27,9 +27,9 @@ 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); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { 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 @@ -47,7 +47,7 @@ struct SingleSubst Iterator glyphs) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (u.format))) return_trace (false); + if (unlikely (!c->extend_min (u.format.v))) return_trace (false); unsigned format = 2; unsigned delta = 0; if (glyphs) @@ -71,8 +71,8 @@ struct SingleSubst if (!hb_all (++(+glyphs), delta, get_delta)) format += 1; } - u.format = format; - switch (u.format) { + u.format.v = format; + switch (u.format.v) { case 1: return_trace (u.format1.serialize (c, + glyphs | hb_map_retains_sorting (hb_first), 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 be6cd820d28..5ee2c1d1f46 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 @@ -123,6 +123,21 @@ struct SingleSubstFormat1_3 return 1; } + void + collect_glyph_alternates (hb_map_t *alternate_count /* IN/OUT */, + hb_map_t *alternate_glyphs /* IN/OUT */) const + { + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + + hb_iter (this+coverage) + | hb_map ([d, mask] (hb_codepoint_t g) { return hb_pair (g, (g + d) & mask); }) + | hb_apply ([&] (const hb_pair_t &p) -> void + { _hb_collect_glyph_alternates_add (p.first, p.second, + alternate_count, alternate_glyphs); }) + ; + } + bool apply (hb_ot_apply_context_t *c) const { TRACE_APPLY (this); 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 e9096460451..0d51d130fee 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 @@ -100,6 +100,17 @@ struct SingleSubstFormat2_4 return 1; } + void + collect_glyph_alternates (hb_map_t *alternate_count /* IN/OUT */, + hb_map_t *alternate_glyphs /* IN/OUT */) const + { + + hb_zip (this+coverage, substitute) + | hb_apply ([&] (const hb_pair_t &p) -> void + { _hb_collect_glyph_alternates_add (p.first, p.second, + alternate_count, alternate_glyphs); }) + ; + } + bool apply (hb_ot_apply_context_t *c) const { TRACE_APPLY (this); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh index 527f64114b4..5cf9eb368aa 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh @@ -29,8 +29,11 @@ #ifndef OT_LAYOUT_TYPES_HH #define OT_LAYOUT_TYPES_HH -using hb_ot_lookup_cache_t = hb_cache_t<15, 8, 7>; -static_assert (sizeof (hb_ot_lookup_cache_t) == 256, ""); +using hb_ot_layout_mapping_cache_t = hb_cache_t<16, 8, 8>; +static_assert (sizeof (hb_ot_layout_mapping_cache_t) == 512, ""); + +using hb_ot_layout_binary_cache_t = hb_cache_t<14, 1, 8>; +static_assert (sizeof (hb_ot_layout_binary_cache_t) == 256, ""); namespace OT { namespace Layout { diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.cc b/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.cc new file mode 100644 index 00000000000..f0e7f934579 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.cc @@ -0,0 +1,421 @@ +#include "VARC.hh" + +#ifndef HB_NO_VAR_COMPOSITES + +#include "../../../hb-draw.hh" +#include "../../../hb-ot-layout-common.hh" +#include "../../../hb-ot-layout-gdef-table.hh" + +namespace OT { + +//namespace Var { + + +#ifndef HB_NO_DRAW + +struct hb_transforming_pen_context_t +{ + hb_transform_t<> transform; + hb_draw_funcs_t *dfuncs; + void *data; + hb_draw_state_t *st; +}; + +static void +hb_transforming_pen_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; + + c->transform.transform_point (to_x, to_y); + + c->dfuncs->move_to (c->data, *c->st, to_x, to_y); +} + +static void +hb_transforming_pen_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; + + c->transform.transform_point (to_x, to_y); + + c->dfuncs->line_to (c->data, *c->st, to_x, to_y); +} + +static void +hb_transforming_pen_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; + + c->transform.transform_point (control_x, control_y); + c->transform.transform_point (to_x, to_y); + + c->dfuncs->quadratic_to (c->data, *c->st, control_x, control_y, to_x, to_y); +} + +static void +hb_transforming_pen_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; + + c->transform.transform_point (control1_x, control1_y); + c->transform.transform_point (control2_x, control2_y); + c->transform.transform_point (to_x, to_y); + + c->dfuncs->cubic_to (c->data, *c->st, control1_x, control1_y, control2_x, control2_y, to_x, to_y); +} + +static void +hb_transforming_pen_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + void *user_data HB_UNUSED) +{ + hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; + + c->dfuncs->close_path (c->data, *c->st); +} + +static inline void free_static_transforming_pen_funcs (); + +static struct hb_transforming_pen_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t +{ + static hb_draw_funcs_t *create () + { + hb_draw_funcs_t *funcs = hb_draw_funcs_create (); + + hb_draw_funcs_set_move_to_func (funcs, hb_transforming_pen_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, hb_transforming_pen_line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, hb_transforming_pen_quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, hb_transforming_pen_cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, hb_transforming_pen_close_path, nullptr, nullptr); + + hb_draw_funcs_make_immutable (funcs); + + hb_atexit (free_static_transforming_pen_funcs); + + return funcs; + } +} static_transforming_pen_funcs; + +static inline +void free_static_transforming_pen_funcs () +{ + static_transforming_pen_funcs.free_instance (); +} + +static hb_draw_funcs_t * +hb_transforming_pen_get_funcs () +{ + return static_transforming_pen_funcs.get_unconst (); +} + +hb_ubytes_t +VarComponent::get_path_at (const hb_varc_context_t &c, + hb_codepoint_t parent_gid, + hb_array_t coords, + hb_transform_t<> total_transform, + hb_ubytes_t total_record, + hb_scalar_cache_t *cache) const +{ + const unsigned char *end = total_record.arrayZ + total_record.length; + const unsigned char *record = total_record.arrayZ; + + auto &VARC = *c.font->face->table.VARC->table; + auto &varStore = &VARC+VARC.varStore; + +#define READ_UINT32VAR(name) \ + HB_STMT_START { \ + if (unlikely (unsigned (end - record) < HBUINT32VAR::min_size)) return hb_ubytes_t (); \ + hb_barrier (); \ + auto &varint = * (const HBUINT32VAR *) record; \ + unsigned size = varint.get_size (); \ + if (unlikely (unsigned (end - record) < size)) return hb_ubytes_t (); \ + name = (uint32_t) varint; \ + record += size; \ + } HB_STMT_END + + uint32_t flags; + READ_UINT32VAR (flags); + + // gid + + hb_codepoint_t gid = 0; + if (flags & (unsigned) flags_t::GID_IS_24BIT) + { + if (unlikely (unsigned (end - record) < HBGlyphID24::static_size)) + return hb_ubytes_t (); + hb_barrier (); + gid = * (const HBGlyphID24 *) record; + record += HBGlyphID24::static_size; + } + else + { + if (unlikely (unsigned (end - record) < HBGlyphID16::static_size)) + return hb_ubytes_t (); + hb_barrier (); + gid = * (const HBGlyphID16 *) record; + record += HBGlyphID16::static_size; + } + + // Condition + bool show = true; + if (flags & (unsigned) flags_t::HAVE_CONDITION) + { + unsigned conditionIndex; + READ_UINT32VAR (conditionIndex); + const auto &condition = (&VARC+VARC.conditionList)[conditionIndex]; + auto instancer = MultiItemVarStoreInstancer(&varStore, nullptr, coords, cache); + show = condition.evaluate (coords.arrayZ, coords.length, &instancer); + } + + // Axis values + + auto &axisIndices = c.scratch.axisIndices; + axisIndices.clear (); + auto &axisValues = c.scratch.axisValues; + axisValues.clear (); + if (flags & (unsigned) flags_t::HAVE_AXES) + { + unsigned axisIndicesIndex; + READ_UINT32VAR (axisIndicesIndex); + axisIndices.extend ((&VARC+VARC.axisIndicesList)[axisIndicesIndex]); + axisValues.resize (axisIndices.length); + const HBUINT8 *p = (const HBUINT8 *) record; + TupleValues::decompile (p, axisValues, (const HBUINT8 *) end); + record = (const unsigned char *) p; + } + + // Apply variations if any + if (flags & (unsigned) flags_t::AXIS_VALUES_HAVE_VARIATION) + { + uint32_t axisValuesVarIdx; + READ_UINT32VAR (axisValuesVarIdx); + if (show && coords && !axisValues.in_error ()) + varStore.get_delta (axisValuesVarIdx, coords, axisValues.as_array (), cache); + } + + auto component_coords = coords; + /* Copying coords is expensive; so we have put an arbitrary + * limit on the max number of coords for now. */ + if ((flags & (unsigned) flags_t::RESET_UNSPECIFIED_AXES) || + coords.length > HB_VAR_COMPOSITE_MAX_AXES) + component_coords = hb_array (c.font->coords, c.font->num_coords); + + // Transform + + uint32_t transformVarIdx = VarIdx::NO_VARIATION; + if (flags & (unsigned) flags_t::TRANSFORM_HAS_VARIATION) + READ_UINT32VAR (transformVarIdx); + +#define PROCESS_TRANSFORM_COMPONENTS \ + HB_STMT_START { \ + PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TRANSLATE_X, translateX); \ + PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TRANSLATE_Y, translateY); \ + PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_ROTATION, rotation); \ + PROCESS_TRANSFORM_COMPONENT (F6DOT10, 1.0f, HAVE_SCALE_X, scaleX); \ + PROCESS_TRANSFORM_COMPONENT (F6DOT10, 1.0f, HAVE_SCALE_Y, scaleY); \ + PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_SKEW_X, skewX); \ + PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_SKEW_Y, skewY); \ + PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TCENTER_X, tCenterX); \ + PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TCENTER_Y, tCenterY); \ + } HB_STMT_END + + hb_transform_decomposed_t<> transform; + + // Read transform components +#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \ + if (flags & (unsigned) flags_t::flag) \ + { \ + static_assert (type::static_size == HBINT16::static_size, ""); \ + if (unlikely (unsigned (end - record) < HBINT16::static_size)) \ + return hb_ubytes_t (); \ + hb_barrier (); \ + transform.name = mult * * (const HBINT16 *) record; \ + record += HBINT16::static_size; \ + } + PROCESS_TRANSFORM_COMPONENTS; +#undef PROCESS_TRANSFORM_COMPONENT + + // Read reserved records + unsigned i = flags & (unsigned) flags_t::RESERVED_MASK; + while (i) + { + HB_UNUSED uint32_t discard; + READ_UINT32VAR (discard); + i &= i - 1; + } + + /* Parsing is over now. */ + + if (show) + { + // Only use coord_setter if there's actually any axis overrides. + coord_setter_t coord_setter (axisIndices ? component_coords : hb_array ()); + // Go backwards, to reduce coord_setter vector reallocations. + for (unsigned i = axisIndices.length; i; i--) + coord_setter[axisIndices[i - 1]] = axisValues[i - 1]; + if (axisIndices) + component_coords = coord_setter.get_coords (); + + // Apply transform variations if any + if (transformVarIdx != VarIdx::NO_VARIATION && coords) + { + float transformValues[9]; + unsigned numTransformValues = 0; +#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \ + if (flags & (unsigned) flags_t::flag) \ + transformValues[numTransformValues++] = transform.name / mult; + PROCESS_TRANSFORM_COMPONENTS; +#undef PROCESS_TRANSFORM_COMPONENT + varStore.get_delta (transformVarIdx, coords, hb_array (transformValues, numTransformValues), cache); + numTransformValues = 0; +#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \ + if (flags & (unsigned) flags_t::flag) \ + transform.name = transformValues[numTransformValues++] * mult; + PROCESS_TRANSFORM_COMPONENTS; +#undef PROCESS_TRANSFORM_COMPONENT + } + + // Divide them by their divisors +#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \ + if (flags & (unsigned) flags_t::flag) \ + { \ + HBINT16 int_v; \ + int_v = roundf (transform.name); \ + type typed_v = * (const type *) &int_v; \ + float float_v = (float) typed_v; \ + transform.name = float_v; \ + } + PROCESS_TRANSFORM_COMPONENTS; +#undef PROCESS_TRANSFORM_COMPONENT + + if (!(flags & (unsigned) flags_t::HAVE_SCALE_Y)) + transform.scaleY = transform.scaleX; + + total_transform.transform (transform.to_transform ()); + total_transform.scale (c.font->x_mult ? 1.f / c.font->x_multf : 0.f, + c.font->y_mult ? 1.f / c.font->y_multf : 0.f); + + bool same_coords = component_coords.length == coords.length && + component_coords.arrayZ == coords.arrayZ; + + c.depth_left--; + VARC.get_path_at (c, gid, + component_coords, total_transform, + parent_gid, + same_coords ? cache : nullptr); + c.depth_left++; + } + +#undef PROCESS_TRANSFORM_COMPONENTS +#undef READ_UINT32VAR + + return hb_ubytes_t (record, end - record); +} + +bool +VARC::get_path_at (const hb_varc_context_t &c, + hb_codepoint_t glyph, + hb_array_t coords, + hb_transform_t<> transform, + hb_codepoint_t parent_glyph, + hb_scalar_cache_t *parent_cache) const +{ + // Don't recurse on the same glyph. + unsigned idx = glyph == parent_glyph ? + NOT_COVERED : + (this+coverage).get_coverage (glyph); + if (idx == NOT_COVERED) + { + if (c.draw_session) + { + // Build a transforming pen to apply the transform. + hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs (); + hb_transforming_pen_context_t context {transform, + c.draw_session->funcs, + c.draw_session->draw_data, + &c.draw_session->st}; + hb_draw_session_t transformer_session {transformer_funcs, &context}; + hb_draw_session_t &shape_draw_session = transform.is_identity () ? *c.draw_session : transformer_session; + + if (c.font->face->table.glyf->get_path_at (c.font, glyph, shape_draw_session, coords, c.scratch.glyf_scratch)) return true; +#ifndef HB_NO_CFF + if (c.font->face->table.cff2->get_path_at (c.font, glyph, shape_draw_session, coords)) return true; + if (c.font->face->table.cff1->get_path (c.font, glyph, shape_draw_session)) return true; // Doesn't have variations +#endif + return false; + } + else if (c.extents) + { + hb_glyph_extents_t glyph_extents; + if (!c.font->face->table.glyf->get_extents_at (c.font, glyph, &glyph_extents, coords)) +#ifndef HB_NO_CFF + if (!c.font->face->table.cff2->get_extents_at (c.font, glyph, &glyph_extents, coords)) + if (!c.font->face->table.cff1->get_extents (c.font, glyph, &glyph_extents)) // Doesn't have variations +#endif + return false; + + hb_extents_t<> comp_extents (glyph_extents); + transform.transform_extents (comp_extents); + c.extents->union_ (comp_extents); + } + return true; + } + + if (c.depth_left <= 0) + return true; + + if (c.edges_left <= 0) + return true; + (c.edges_left)--; + + hb_decycler_node_t node (c.decycler); + if (unlikely (!node.visit (glyph))) + return true; + + hb_ubytes_t record = (this+glyphRecords)[idx]; + + hb_scalar_cache_t static_cache; + hb_scalar_cache_t *cache = parent_cache ? + parent_cache : + (this+varStore).create_cache (&static_cache); + + transform.scale (c.font->x_multf, c.font->y_multf); + + VarCompositeGlyph::get_path_at (c, + glyph, + coords, transform, + record, + cache); + + if (cache != parent_cache) + (this+varStore).destroy_cache (cache, &static_cache); + + return true; +} + +#endif + +//} // namespace Var +} // namespace OT + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.hh b/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.hh index 2ea1b6bca32..6b40f044556 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.hh @@ -32,7 +32,7 @@ struct hb_varc_context_t { hb_font_t *font; hb_draw_session_t *draw_session; - hb_extents_t *extents; + hb_extents_t<> *extents; mutable hb_decycler_t decycler; mutable signed edges_left; mutable signed depth_left; @@ -65,9 +65,9 @@ struct VarComponent get_path_at (const hb_varc_context_t &c, hb_codepoint_t parent_gid, hb_array_t coords, - hb_transform_t transform, + hb_transform_t<> transform, hb_ubytes_t record, - VarRegionList::cache_t *cache = nullptr) const; + hb_scalar_cache_t *cache = nullptr) const; }; struct VarCompositeGlyph @@ -76,9 +76,9 @@ struct VarCompositeGlyph get_path_at (const hb_varc_context_t &c, hb_codepoint_t gid, hb_array_t coords, - hb_transform_t transform, + hb_transform_t<> transform, hb_ubytes_t record, - VarRegionList::cache_t *cache) + hb_scalar_cache_t *cache) { while (record) { @@ -104,9 +104,9 @@ struct VARC get_path_at (const hb_varc_context_t &c, hb_codepoint_t gid, hb_array_t coords, - hb_transform_t transform = HB_TRANSFORM_IDENTITY, + hb_transform_t<> transform = HB_TRANSFORM_IDENTITY, hb_codepoint_t parent_gid = HB_CODEPOINT_INVALID, - VarRegionList::cache_t *parent_cache = nullptr) const; + hb_scalar_cache_t *parent_cache = nullptr) const; bool get_path (hb_font_t *font, @@ -129,7 +129,7 @@ struct VARC bool get_extents (hb_font_t *font, hb_codepoint_t gid, - hb_extents_t *extents, + hb_extents_t<> *extents, hb_varc_scratch_t &scratch) const { hb_varc_context_t c {font, @@ -194,9 +194,10 @@ struct VARC hb_codepoint_t gid, hb_glyph_extents_t *extents) const { +#ifndef HB_NO_DRAW if (!table->has_data ()) return false; - hb_extents_t f_extents; + hb_extents_t<> f_extents; auto *scratch = acquire_scratch (); if (unlikely (!scratch)) return true; @@ -207,6 +208,9 @@ struct VARC *extents = f_extents.to_glyph_extents (font->x_scale < 0, font->y_scale < 0); return ret; +#else + return false; +#endif } private: 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 1805df262aa..8f5287e9457 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh @@ -102,17 +102,15 @@ struct Glyph if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; hb_array_t phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); { + // Duplicated code. int lsb = 0; - int h_delta = face->table.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ? - (int) header->xMin - lsb : 0; + face->table.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb); + int h_delta = (int) header->xMin - lsb; HB_UNUSED int tsb = 0; - int v_orig = (int) header->yMax + #ifndef HB_NO_VERTICAL - ((void) face->table.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb) -#else - 0 + face->table.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb); #endif - ; + int v_orig = (int) header->yMax + tsb; unsigned h_adv = face->table.hmtx->get_advance_without_var_unscaled (gid); unsigned v_adv = #ifndef HB_NO_VERTICAL @@ -314,6 +312,7 @@ struct Glyph bool use_my_metrics = true, bool phantom_only = false, hb_array_t coords = hb_array_t (), + hb_scalar_cache_t *gvar_cache = nullptr, unsigned int depth = 0, unsigned *edge_count = nullptr) const { @@ -328,7 +327,7 @@ struct Glyph head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth); } - if (!coords) + if (!coords && font->has_nonzero_coords) coords = hb_array (font->coords, font->num_coords); contour_point_vector_t &points = type == SIMPLE ? all_points : scratch.comp_points; @@ -357,17 +356,15 @@ struct Glyph if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; hb_array_t phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); { + // Duplicated code. int lsb = 0; - int h_delta = glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ? - (int) header->xMin - lsb : 0; + glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb); + int h_delta = (int) header->xMin - lsb; HB_UNUSED int tsb = 0; - int v_orig = (int) header->yMax + #ifndef HB_NO_VERTICAL - ((void) glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb) -#else - 0 + glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb); #endif - ; + int v_orig = (int) header->yMax + tsb; unsigned h_adv = glyf_accelerator.hmtx->get_advance_without_var_unscaled (gid); unsigned v_adv = #ifndef HB_NO_VERTICAL @@ -383,7 +380,7 @@ struct Glyph } #ifndef HB_NO_VAR - if (coords) + if (hb_any (coords)) { #ifndef HB_NO_BEYOND_64K if (glyf_accelerator.GVAR->has_data ()) @@ -391,6 +388,7 @@ struct Glyph coords, points.as_array ().sub_array (old_length), scratch, + gvar_cache, phantom_only && type == SIMPLE); else #endif @@ -398,6 +396,7 @@ struct Glyph coords, points.as_array ().sub_array (old_length), scratch, + gvar_cache, phantom_only && type == SIMPLE); } #endif @@ -447,6 +446,7 @@ struct Glyph use_my_metrics, phantom_only, coords, + gvar_cache, depth + 1, edge_count))) { @@ -533,7 +533,11 @@ struct Glyph 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. */ + if (type == EMPTY) + { + *extents = {0, 0, 0, 0}; + return true; /* Empty glyph; zero extents. */ + } return header->get_extents_without_var_scaled (font, glyf_accelerator, gid, extents); } 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 601e1303792..507c94f7f3d 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh @@ -189,7 +189,7 @@ struct SimpleGlyph unsigned old_length = points.length; points.alloc (points.length + num_points + 4); // Allocate for phantom points, to avoid a possible copy - if (unlikely (!points.resize (points.length + num_points, false))) return false; + if (unlikely (!points.resize_dirty (points.length + num_points))) return false; auto points_ = points.as_array ().sub_array (old_length); if (!phantom_only) hb_memset (points_.arrayZ, 0, sizeof (contour_point_t) * num_points); 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 d9e5fedfa92..3fe2506bec9 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh @@ -220,7 +220,8 @@ struct glyf_accelerator_t template bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer, hb_array_t coords, - hb_glyf_scratch_t &scratch) const + hb_glyf_scratch_t &scratch, + hb_scalar_cache_t *gvar_cache = nullptr) const { if (gid >= num_glyphs) return false; @@ -228,7 +229,7 @@ struct glyf_accelerator_t all_points.resize (0); bool phantom_only = !consumer.is_consuming_contour_points (); - if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, scratch, nullptr, nullptr, nullptr, true, true, phantom_only, coords))) + if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, scratch, nullptr, nullptr, nullptr, true, true, phantom_only, coords, gvar_cache))) return false; unsigned count = all_points.length; @@ -371,28 +372,28 @@ struct glyf_accelerator_t contour_point_t *get_phantoms_sink () { return phantoms; } }; +#ifndef HB_NO_VAR unsigned - get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const + get_advance_with_var_unscaled (hb_codepoint_t gid, + hb_font_t *font, + bool is_vertical, + hb_glyf_scratch_t &scratch, + hb_scalar_cache_t *gvar_cache = nullptr) const { if (unlikely (gid >= num_glyphs)) return 0; bool success = false; contour_point_t phantoms[glyf_impl::PHANTOM_COUNT]; - if (font->num_coords) - { - hb_glyf_scratch_t scratch; - success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false), - hb_array (font->coords, font->num_coords), - scratch); - } - + success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false), + hb_array (font->coords, + font->has_nonzero_coords ? font->num_coords : 0), + scratch, gvar_cache); if (unlikely (!success)) - return -#ifndef HB_NO_VERTICAL - is_vertical ? vmtx->get_advance_without_var_unscaled (gid) : -#endif - hmtx->get_advance_without_var_unscaled (gid); + { + unsigned upem = font->face->get_upem (); + return is_vertical ? upem : upem / 2; + } float result = is_vertical ? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y @@ -400,40 +401,38 @@ struct glyf_accelerator_t return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2); } - bool get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical, int *lsb) const + float + get_v_origin_with_var_unscaled (hb_codepoint_t gid, + hb_font_t *font, + hb_glyf_scratch_t &scratch, + hb_scalar_cache_t *gvar_cache = nullptr) const { - if (unlikely (gid >= num_glyphs)) return false; + if (unlikely (gid >= num_glyphs)) return 0; + + bool success = false; - hb_glyph_extents_t extents; - hb_glyf_scratch_t scratch; contour_point_t phantoms[glyf_impl::PHANTOM_COUNT]; - if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false), - hb_array (font->coords, font->num_coords), - scratch))) - return false; + success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false), + hb_array (font->coords, + font->has_nonzero_coords ? font->num_coords : 0), + scratch, gvar_cache); + if (unlikely (!success)) + { + return font->face->get_upem (); + } - *lsb = is_vertical - ? roundf (phantoms[glyf_impl::PHANTOM_TOP].y) - extents.y_bearing - : roundf (phantoms[glyf_impl::PHANTOM_LEFT].x); - return true; + return phantoms[glyf_impl::PHANTOM_TOP].y; } #endif - - bool get_leading_bearing_without_var_unscaled (hb_codepoint_t gid, bool is_vertical, int *lsb) const - { - if (unlikely (gid >= num_glyphs)) return false; - if (is_vertical) return false; // TODO Humm, what to do here? - - *lsb = glyph_for_gid (gid).get_header ()->xMin; - return true; - } +#endif public: bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const - { return get_extents_at (font, gid, extents, hb_array (font->coords, font->num_coords)); } + { return get_extents_at (font, gid, extents, hb_array (font->coords, + font->has_nonzero_coords ? font->num_coords : 0)); } bool get_extents_at (hb_font_t *font, hb_codepoint_t gid, @@ -445,12 +444,15 @@ struct glyf_accelerator_t #ifndef HB_NO_VAR if (coords) { - hb_glyf_scratch_t scratch; - return get_points (font, - gid, - points_aggregator_t (font, extents, nullptr, true), - coords, - scratch); + hb_glyf_scratch_t *scratch = acquire_scratch (); + if (unlikely (!scratch)) return false; + bool ret = get_points (font, + gid, + points_aggregator_t (font, extents, nullptr, true), + coords, + *scratch); + release_scratch (scratch); + return ret; } #endif return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents); @@ -485,33 +487,20 @@ struct glyf_accelerator_t } bool - get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const + get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session, hb_scalar_cache_t *gvar_cache = nullptr) const { if (!has_data ()) return false; - hb_glyf_scratch_t *scratch; - - // Borrow the cached strach buffer. - { - scratch = cached_scratch.get_acquire (); - if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr))) - { - scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t)); - if (unlikely (!scratch)) - return true; - } - } + hb_glyf_scratch_t *scratch = acquire_scratch (); + if (unlikely (!scratch)) return true; bool ret = get_points (font, gid, glyf_impl::path_builder_t (font, draw_session), - hb_array (font->coords, font->num_coords), - *scratch); + hb_array (font->coords, + font->has_nonzero_coords ? font->num_coords : 0), + *scratch, + gvar_cache); - // Put it back. - if (!cached_scratch.cmpexch (nullptr, scratch)) - { - scratch->~hb_glyf_scratch_t (); - hb_free (scratch); - } + release_scratch (scratch); return ret; } @@ -519,12 +508,38 @@ struct glyf_accelerator_t bool get_path_at (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session, hb_array_t coords, - hb_glyf_scratch_t &scratch) const + hb_glyf_scratch_t &scratch, + hb_scalar_cache_t *gvar_cache = nullptr) const { if (!has_data ()) return false; return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session), coords, - scratch); + scratch, + gvar_cache); + } + + + hb_glyf_scratch_t *acquire_scratch () const + { + if (!has_data ()) return nullptr; + hb_glyf_scratch_t *scratch = cached_scratch.get_acquire (); + if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr))) + { + scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t)); + if (unlikely (!scratch)) + return nullptr; + } + return scratch; + } + void release_scratch (hb_glyf_scratch_t *scratch) const + { + if (!scratch) + return; + if (!cached_scratch.cmpexch (nullptr, scratch)) + { + scratch->~hb_glyf_scratch_t (); + hb_free (scratch); + } } #ifndef HB_NO_VAR diff --git a/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh index da6378820bb..d1f38b9de8a 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh @@ -74,7 +74,7 @@ struct ClassDef : public OT::ClassDef class_def_link->width = SmallTypes::size; class_def_link->objidx = class_def_prime_id; class_def_link->position = link_position; - class_def_prime_vertex.add_parent (parent_id); + class_def_prime_vertex.add_parent (parent_id, false); return true; } @@ -117,7 +117,7 @@ struct ClassDef : public OT::ClassDef int64_t vertex_len = vertex.obj.tail - vertex.obj.head; if (vertex_len < OT::ClassDef::min_size) return false; hb_barrier (); - switch (u.format) + switch (u.format.v) { case 1: return ((ClassDefFormat1*)this)->sanitize (vertex); case 2: return ((ClassDefFormat2*)this)->sanitize (vertex); diff --git a/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh index 61ca063e345..46c703524d9 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh @@ -32,29 +32,27 @@ 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; - hb_barrier (); - return vertex_len >= min_size + glyphArray.get_size () - glyphArray.len.get_size (); - } -}; +static bool sanitize ( + const OT::Layout::Common::CoverageFormat1_3* thiz, + graph_t::vertex_t& vertex +) { + 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; + hb_barrier (); + return vertex_len >= min_size + thiz->glyphArray.get_size () - thiz->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; - hb_barrier (); - return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size (); - } -}; +static bool sanitize ( + const OT::Layout::Common::CoverageFormat2_4* thiz, + graph_t::vertex_t& vertex +) { + 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; + hb_barrier (); + return vertex_len >= min_size + thiz->rangeRecord.get_size () - thiz->rangeRecord.len.get_size (); +} struct Coverage : public OT::Layout::Common::Coverage { @@ -98,11 +96,33 @@ struct Coverage : public OT::Layout::Common::Coverage coverage_link->width = SmallTypes::size; coverage_link->objidx = coverage_prime_id; coverage_link->position = link_position; - coverage_prime_vertex.add_parent (parent_id); + coverage_prime_vertex.add_parent (parent_id, false); return (Coverage*) coverage_prime_vertex.obj.head; } + // Filter an existing coverage table to glyphs at indices [start, end) and replace it with the filtered version. + static bool filter_coverage (gsubgpos_graph_context_t& c, + unsigned existing_coverage, + unsigned start, unsigned end) { + unsigned coverage_size = c.graph.vertices_[existing_coverage].table_size (); + auto& coverage_v = c.graph.vertices_[existing_coverage]; + Coverage* coverage_table = (Coverage*) coverage_v.obj.head; + if (!coverage_table || !coverage_table->sanitize (coverage_v)) + return false; + + 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 make_coverage (c, new_coverage, existing_coverage, coverage_size * 2 + 100); + } + + // Replace the coverage table at dest obj with one covering 'glyphs'. template static bool make_coverage (gsubgpos_graph_context_t& c, It glyphs, @@ -141,10 +161,10 @@ struct Coverage : public OT::Layout::Common::Coverage int64_t vertex_len = vertex.obj.tail - vertex.obj.head; if (vertex_len < OT::Layout::Common::Coverage::min_size) return false; hb_barrier (); - switch (u.format) + switch (u.format.v) { - case 1: return ((CoverageFormat1*)this)->sanitize (vertex); - case 2: return ((CoverageFormat2*)this)->sanitize (vertex); + case 1: return graph::sanitize ((const OT::Layout::Common::CoverageFormat1_3*) this, vertex); + case 2: return graph::sanitize ((const OT::Layout::Common::CoverageFormat2_4*) this, vertex); #ifndef HB_NO_BEYOND_64K // Not currently supported case 3: diff --git a/src/java.desktop/share/native/libharfbuzz/graph/graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/graph.hh index ed1026f5866..78ae1a9dd5b 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/graph.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/graph.hh @@ -50,6 +50,7 @@ struct graph_t private: unsigned incoming_edges_ = 0; unsigned single_parent = (unsigned) -1; + bool has_incoming_virtual_edges_ = false; hb_hashmap_t parents; public: @@ -66,6 +67,11 @@ struct graph_t return parents.in_error (); } + bool has_incoming_virtual_edges () const + { + return has_incoming_virtual_edges_; + } + bool link_positions_valid (unsigned num_objects, bool removed_nil) { hb_set_t assigned_bytes; @@ -121,7 +127,9 @@ struct graph_t } } - bool equals (const vertex_t& other, + bool equals (unsigned this_index, + unsigned other_index, + const vertex_t& other, const graph_t& graph, const graph_t& other_graph, unsigned depth) const @@ -129,8 +137,10 @@ struct graph_t if (!(as_bytes () == other.as_bytes ())) { DEBUG_MSG (SUBSET_REPACK, nullptr, - "vertex [%lu] bytes != [%lu] bytes, depth = %u", + "vertex %u [%lu bytes] != %u [%lu bytes], depth = %u", + this_index, (unsigned long) table_size (), + other_index, (unsigned long) other.table_size (), depth); @@ -162,6 +172,7 @@ struct graph_t hb_swap (a.single_parent, b.single_parent); hb_swap (a.parents, b.parents); hb_swap (a.incoming_edges_, b.incoming_edges_); + hb_swap (a.has_incoming_virtual_edges_, b.has_incoming_virtual_edges_); hb_swap (a.start, b.start); hb_swap (a.end, b.end); hb_swap (a.priority, b.priority); @@ -207,13 +218,16 @@ struct graph_t void reset_parents () { incoming_edges_ = 0; + has_incoming_virtual_edges_ = false; single_parent = (unsigned) -1; parents.reset (); } - void add_parent (unsigned parent_index) + void add_parent (unsigned parent_index, bool is_virtual) { assert (parent_index != (unsigned) -1); + has_incoming_virtual_edges_ |= is_virtual; + if (incoming_edges_ == 0) { single_parent = parent_index; @@ -408,7 +422,7 @@ struct graph_t link_a.bias != link_b.bias) return false; - if (!graph.vertices_[link_a.objidx].equals ( + if (!graph.vertices_[link_a.objidx].equals (link_a.objidx, link_b.objidx, other_graph.vertices_[link_b.objidx], graph, other_graph, depth + 1)) return false; @@ -456,8 +470,12 @@ struct graph_t num_roots_for_space_.push (1); bool removed_nil = false; vertices_.alloc (objects.length); - vertices_scratch_.alloc (objects.length); + ordering_.resize (objects.length); + ordering_scratch_.alloc (objects.length); + unsigned count = objects.length; + unsigned order = objects.length; + unsigned skip = 0; for (unsigned i = 0; i < count; i++) { // If this graph came from a serialization buffer object 0 is the @@ -465,6 +483,9 @@ struct graph_t if (i == 0 && !objects.arrayZ[i]) { removed_nil = true; + order--; + ordering_.resize(objects.length - 1); + skip++; continue; } @@ -474,6 +495,12 @@ struct graph_t check_success (v->link_positions_valid (count, removed_nil)); + // To start we set the ordering to match the provided objects + // list. Note: objects are provided to us in reverse order (ie. + // the last object is the root). + unsigned obj_idx = i - skip; + ordering_[--order] = obj_idx; + if (!removed_nil) continue; // Fix indices to account for removed nil object. for (auto& l : v->obj.all_links_writer ()) { @@ -490,17 +517,20 @@ struct graph_t bool operator== (const graph_t& other) const { - return root ().equals (other.root (), *this, other, 0); + return root ().equals (root_idx(), other.root_idx(), other.root (), *this, other, 0); } void print () const { - for (int i = vertices_.length - 1; i >= 0; i--) + for (unsigned id : ordering_) { - const auto& v = vertices_[i]; - printf("%d: %u [", i, (unsigned int)v.table_size()); + const auto& v = vertices_[id]; + printf("%u: %u [", id, (unsigned int)v.table_size()); for (const auto &l : v.obj.real_links) { printf("%u, ", l.objidx); } + for (const auto &l : v.obj.virtual_links) { + printf("v%u, ", l.objidx); + } printf("]\n"); } } @@ -516,6 +546,7 @@ struct graph_t { return !successful || vertices_.in_error () || + ordering_.in_error() || num_roots_for_space_.in_error (); } @@ -526,10 +557,10 @@ struct graph_t unsigned root_idx () const { - // Object graphs are in reverse order, the first object is at the end - // of the vector. Since the graph is topologically sorted it's safe to + // First element of ordering_ is the root. + // Since the graph is topologically sorted it's safe to // assume the first object has no incoming edges. - return vertices_.length - 1; + return ordering_[0]; } const hb_serialize_context_t::object_t& object (unsigned i) const @@ -556,7 +587,7 @@ struct graph_t link->width = 2; link->objidx = child_id; link->position = (char*) offset - (char*) v.obj.head; - vertices_[child_id].add_parent (parent_id); + vertices_[child_id].add_parent (parent_id, false); } /* @@ -587,55 +618,51 @@ struct graph_t hb_priority_queue_t queue; queue.alloc (vertices_.length); - hb_vector_t &sorted_graph = vertices_scratch_; - if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return; - hb_vector_t id_map; - if (unlikely (!check_success (id_map.resize (vertices_.length)))) return; + hb_vector_t &new_ordering = ordering_scratch_; + if (unlikely (!check_success (new_ordering.resize (vertices_.length)))) return; hb_vector_t removed_edges; if (unlikely (!check_success (removed_edges.resize (vertices_.length)))) return; update_parents (); queue.insert (root ().modified_distance (0), root_idx ()); - int new_id = root_idx (); unsigned order = 1; + unsigned pos = 0; while (!queue.in_error () && !queue.is_empty ()) { unsigned next_id = queue.pop_minimum().second; - sorted_graph[new_id] = std::move (vertices_[next_id]); - const vertex_t& next = sorted_graph[new_id]; - - if (unlikely (!check_success(new_id >= 0))) { + if (unlikely (!check_success(pos < new_ordering.length))) { // 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--; + new_ordering[pos++] = next_id; + const vertex_t& next = vertices_[next_id]; for (const auto& link : next.obj.all_links ()) { removed_edges[link.objidx]++; - if (!(vertices_[link.objidx].incoming_edges () - removed_edges[link.objidx])) + const auto& v = vertices_[link.objidx]; + if (!(v.incoming_edges () - removed_edges[link.objidx])) // Add the order that the links were encountered to the priority. // This ensures that ties between priorities objects are broken in a consistent // way. More specifically this is set up so that if a set of objects have the same // distance they'll be added to the topological order in the order that they are // referenced from the parent object. - queue.insert (vertices_[link.objidx].modified_distance (order++), + queue.insert (v.modified_distance (order++), link.objidx); } } check_success (!queue.in_error ()); - check_success (!sorted_graph.in_error ()); + check_success (!new_ordering.in_error ()); - check_success (remap_all_obj_indices (id_map, &sorted_graph)); - vertices_ = std::move (sorted_graph); + hb_swap (ordering_, new_ordering); - if (!check_success (new_id == -1)) + if (!check_success (pos == vertices_.length)) { print_orphaned_nodes (); + } } /* @@ -645,8 +672,8 @@ struct graph_t */ void find_space_roots (hb_set_t& visited, hb_set_t& roots) { - int root_index = (int) root_idx (); - for (int i = root_index; i >= 0; i--) + unsigned root_index = root_idx (); + for (unsigned i : ordering_) { if (visited.has (i)) continue; @@ -829,7 +856,6 @@ struct graph_t if (subgraph.in_error ()) return false; - unsigned original_root_idx = root_idx (); hb_map_t index_map; bool made_changes = false; for (auto entry : subgraph.iter ()) @@ -852,14 +878,6 @@ struct graph_t if (!made_changes) return false; - if (original_root_idx != root_idx () - && parents.has (original_root_idx)) - { - // If the root idx has changed since parents was determined, update root idx in parents - parents.add (root_idx ()); - parents.del (original_root_idx); - } - auto new_subgraph = + subgraph.keys () | hb_map([&] (uint32_t node_idx) { @@ -943,12 +961,14 @@ struct graph_t /* * Moves the child of old_parent_idx pointed to by old_offset to a new * vertex at the new_offset. + * + * Returns the id of the child node that was moved. */ template - void move_child (unsigned old_parent_idx, - const O* old_offset, - unsigned new_parent_idx, - const O* new_offset) + unsigned move_child (unsigned old_parent_idx, + const O* old_offset, + unsigned new_parent_idx, + const O* new_offset) { distance_invalid = true; positions_invalid = true; @@ -965,10 +985,56 @@ struct graph_t new_link->position = (const char*) new_offset - (const char*) new_v.obj.head; auto& child = vertices_[child_id]; - child.add_parent (new_parent_idx); + child.add_parent (new_parent_idx, false); old_v.remove_real_link (child_id, old_offset); child.remove_parent (old_parent_idx); + + return child_id; + } + + /* + * Moves all outgoing links in old parent that have + * a link position between [old_post_start, old_pos_end) + * to the new parent. Links are placed serially in the new + * parent starting at new_pos_start. + */ + template + void move_children (unsigned old_parent_idx, + unsigned old_pos_start, + unsigned old_pos_end, + unsigned new_parent_idx, + unsigned new_pos_start) + { + distance_invalid = true; + positions_invalid = true; + + auto& old_v = vertices_[old_parent_idx]; + auto& new_v = vertices_[new_parent_idx]; + + hb_vector_t old_links; + for (const auto& l : old_v.obj.real_links) + { + if (l.position < old_pos_start || l.position >= old_pos_end) + { + old_links.push(l); + continue; + } + + unsigned array_pos = l.position - old_pos_start; + + unsigned child_id = l.objidx; + auto* new_link = new_v.obj.real_links.push (); + new_link->width = O::static_size; + new_link->objidx = child_id; + new_link->position = new_pos_start + array_pos; + + auto& child = vertices_[child_id]; + child.add_parent (new_parent_idx, false); + child.remove_parent (old_parent_idx); + } + + old_v.obj.real_links = std::move (old_links); } /* @@ -1000,8 +1066,11 @@ struct graph_t distance_invalid = true; auto* clone = vertices_.push (); + unsigned clone_idx = vertices_.length - 1; + ordering_.push(clone_idx); + auto& child = vertices_[node_idx]; - if (vertices_.in_error ()) { + if (vertices_.in_error () || ordering_.in_error()) { return -1; } @@ -1011,51 +1080,23 @@ struct graph_t clone->space = child.space; clone->reset_parents (); - unsigned clone_idx = vertices_.length - 2; for (const auto& l : child.obj.real_links) { clone->obj.real_links.push (l); - vertices_[l.objidx].add_parent (clone_idx); + vertices_[l.objidx].add_parent (clone_idx, false); } for (const auto& l : child.obj.virtual_links) { clone->obj.virtual_links.push (l); - vertices_[l.objidx].add_parent (clone_idx); + vertices_[l.objidx].add_parent (clone_idx, true); } check_success (!clone->obj.real_links.in_error ()); check_success (!clone->obj.virtual_links.in_error ()); - // 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; } - /* - * 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. - * - * Returns the index of the newly created duplicate. - * - * If the child_idx only has incoming edges from parent_idx, this - * will do nothing and return the original 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 @@ -1073,10 +1114,15 @@ struct graph_t const auto& child = vertices_[child_idx]; unsigned links_to_child = child.incoming_edges_from_parent(parent_idx); - if (child.incoming_edges () <= links_to_child) + if (child.incoming_edges () <= links_to_child || child.has_incoming_virtual_edges()) { // Can't duplicate this node, doing so would orphan the original one as all remaining links // to child are from parent. + // + // We don't allow duplication of nodes with incoming virtual edges because we don't track + // the number of virtual vs real incoming edges. As a result we can't tell if a node + // with virtual edges may end up orphaned by duplication (ie. where one copy is only pointed + // to by virtual edges). DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %u => %u", parent_idx, child_idx); return -1; @@ -1091,12 +1137,15 @@ struct graph_t if (parent_idx == clone_idx) parent_idx++; auto& parent = vertices_[parent_idx]; + unsigned count = 0; + unsigned num_real = parent.obj.real_links.length; for (auto& l : parent.obj.all_links_writer ()) { + count++; if (l.objidx != child_idx) continue; - reassign_link (l, parent_idx, clone_idx); + reassign_link (l, parent_idx, clone_idx, count > num_real); } return clone_idx; @@ -1129,10 +1178,15 @@ struct graph_t links_to_child += child.incoming_edges_from_parent(parent_idx); } - if (child.incoming_edges () <= links_to_child) + if (child.incoming_edges () <= links_to_child || child.has_incoming_virtual_edges()) { // Can't duplicate this node, doing so would orphan the original one as all remaining links // to child are from parent. + // + // We don't allow duplication of nodes with incoming virtual edges because we don't track + // the number of virtual vs real incoming edges. As a result we can't tell if a node + // with virtual edges may end up orphaned by duplication (ie. where one copy is only pointed + // to by virtual edges). DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %u, ..., %u => %u", first_parent, last_parent, child_idx); return -1; } @@ -1146,12 +1200,15 @@ struct graph_t // duplicate shifts the root node idx, so if parent_idx was root update it. if (parent_idx == clone_idx) parent_idx++; auto& parent = vertices_[parent_idx]; + unsigned count = 0; + unsigned num_real = parent.obj.real_links.length; for (auto& l : parent.obj.all_links_writer ()) { + count++; if (l.objidx != child_idx) continue; - reassign_link (l, parent_idx, clone_idx); + reassign_link (l, parent_idx, clone_idx, count > num_real); } } @@ -1168,7 +1225,10 @@ struct graph_t distance_invalid = true; auto* clone = vertices_.push (); - if (vertices_.in_error ()) { + unsigned clone_idx = vertices_.length - 1; + ordering_.push(clone_idx); + + if (vertices_.in_error () || ordering_.in_error()) { return -1; } @@ -1177,20 +1237,37 @@ struct graph_t 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; } + /* + * Creates a new child node and remap the old child to it. + * + * Returns the index of the newly created child. + * + */ + unsigned remap_child (unsigned parent_idx, unsigned old_child_idx) + { + unsigned new_child_idx = duplicate (old_child_idx); + if (new_child_idx == (unsigned) -1) return -1; + + auto& parent = vertices_[parent_idx]; + for (auto& l : parent.obj.real_links) + { + if (l.objidx != old_child_idx) + continue; + reassign_link (l, parent_idx, new_child_idx, false); + } + + for (auto& l : parent.obj.virtual_links) + { + if (l.objidx != old_child_idx) + continue; + reassign_link (l, parent_idx, new_child_idx, true); + } + return new_child_idx; + } + /* * Raises the sorting priority of all children. */ @@ -1279,6 +1356,7 @@ struct graph_t if (!DEBUG_ENABLED(SUBSET_REPACK)) return; DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected."); + parents_invalid = true; update_parents(); @@ -1348,7 +1426,8 @@ struct graph_t size_t total_size = 0; unsigned count = vertices_.length; for (unsigned i = 0; i < count; i++) { - size_t size = vertices_.arrayZ[i].obj.tail - vertices_.arrayZ[i].obj.head; + const auto& obj = vertices_.arrayZ[i].obj; + size_t size = obj.tail - obj.head; total_size += size; } return total_size; @@ -1398,8 +1477,11 @@ struct graph_t for (unsigned p = 0; p < count; p++) { - for (auto& l : vertices_.arrayZ[p].obj.all_links ()) - vertices_[l.objidx].add_parent (p); + for (auto& l : vertices_.arrayZ[p].obj.real_links) + vertices_[l.objidx].add_parent (p, false); + + for (auto& l : vertices_.arrayZ[p].obj.virtual_links) + vertices_[l.objidx].add_parent (p, true); } for (unsigned i = 0; i < count; i++) @@ -1418,7 +1500,7 @@ struct graph_t if (!positions_invalid) return; unsigned current_pos = 0; - for (int i = root_idx (); i >= 0; i--) + for (unsigned i : ordering_) { auto& v = vertices_[i]; v.start = current_pos; @@ -1450,11 +1532,11 @@ struct graph_t unsigned count = vertices_.length; for (unsigned i = 0; i < count; i++) vertices_.arrayZ[i].distance = hb_int_max (int64_t); - vertices_.tail ().distance = 0; + vertices_[root_idx ()].distance = 0; hb_priority_queue_t queue; queue.alloc (count); - queue.insert (0, vertices_.length - 1); + queue.insert (0, root_idx ()); hb_vector_t visited; visited.resize (vertices_.length); @@ -1464,22 +1546,23 @@ struct graph_t unsigned next_idx = queue.pop_minimum ().second; if (visited[next_idx]) continue; const auto& next = vertices_[next_idx]; - int64_t next_distance = vertices_[next_idx].distance; + int64_t next_distance = next.distance; visited[next_idx] = true; for (const auto& link : next.obj.all_links ()) { if (visited[link.objidx]) continue; - const auto& child = vertices_.arrayZ[link.objidx].obj; + auto& child_v = vertices_.arrayZ[link.objidx]; + const auto& child = child_v.obj; unsigned link_width = link.width ? link.width : 4; // treat virtual offsets as 32 bits wide int64_t child_weight = (child.tail - child.head) + - ((int64_t) 1 << (link_width * 8)) * (vertices_.arrayZ[link.objidx].space + 1); + ((int64_t) 1 << (link_width * 8)) * (child_v.space + 1); int64_t child_distance = next_distance + child_weight; - if (child_distance < vertices_.arrayZ[link.objidx].distance) + if (child_distance < child_v.distance) { - vertices_.arrayZ[link.objidx].distance = child_distance; + child_v.distance = child_distance; queue.insert (child_distance, link.objidx); } } @@ -1502,12 +1585,13 @@ struct graph_t */ void reassign_link (hb_serialize_context_t::object_t::link_t& link, unsigned parent_idx, - unsigned new_idx) + unsigned new_idx, + bool is_virtual) { unsigned old_idx = link.objidx; link.objidx = new_idx; vertices_[old_idx].remove_parent (parent_idx); - vertices_[new_idx].add_parent (parent_idx); + vertices_[new_idx].add_parent (parent_idx, is_virtual); } /* @@ -1521,36 +1605,21 @@ struct graph_t if (!id_map) return; for (unsigned i : subgraph) { - for (auto& link : vertices_[i].obj.all_links_writer ()) + auto& obj = vertices_[i].obj; + unsigned num_real = obj.real_links.length; + unsigned count = 0; + for (auto& link : obj.all_links_writer ()) { + count++; const uint32_t *v; if (!id_map.has (link.objidx, &v)) continue; - if (only_wide && !(link.width == 4 && !link.is_signed)) continue; + if (only_wide && (link.is_signed || (link.width != 4 && link.width != 3))) continue; - reassign_link (link, i, *v); + reassign_link (link, i, *v, count > num_real); } } } - /* - * Updates all objidx's in all links using the provided mapping. - */ - bool remap_all_obj_indices (const hb_vector_t& id_map, - hb_vector_t* sorted_graph) const - { - unsigned count = sorted_graph->length; - for (unsigned i = 0; i < count; i++) - { - if (!(*sorted_graph)[i].remap_parents (id_map)) - return false; - for (auto& link : sorted_graph->arrayZ[i].obj.all_links_writer ()) - { - link.objidx = id_map[link.objidx]; - } - } - return true; - } - /* * Finds all nodes in targets that are reachable from start_idx, nodes in visited will be skipped. * For this search the graph is treated as being undirected. @@ -1586,7 +1655,16 @@ struct graph_t public: // TODO(garretrieger): make private, will need to move most of offset overflow code into graph. hb_vector_t vertices_; - hb_vector_t vertices_scratch_; + + // Specifies the current topological ordering of this graph + // + // ordering_[pos] = obj index + // + // specifies that the 'pos'th spot is filled by the object + // given by obj index. + hb_vector_t ordering_; + hb_vector_t ordering_scratch_; + private: bool parents_invalid; bool distance_invalid; diff --git a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.hh b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.hh index b25d538fe3d..8969d007879 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.hh @@ -41,6 +41,7 @@ struct gsubgpos_graph_context_t unsigned lookup_list_index; hb_hashmap_t lookups; hb_hashmap_t subtable_to_extension; + hb_hashmap_t> split_subtables; HB_INTERNAL gsubgpos_graph_context_t (hb_tag_t table_tag_, graph_t& graph_); diff --git a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh index 7ad2ba430b7..ea6bcd239a0 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh @@ -27,9 +27,11 @@ #include "graph.hh" #include "../hb-ot-layout-gsubgpos.hh" #include "../OT/Layout/GSUB/ExtensionSubst.hh" +#include "../OT/Layout/GSUB/SubstLookupSubTable.hh" #include "gsubgpos-context.hh" #include "pairpos-graph.hh" #include "markbasepos-graph.hh" +#include "ligature-graph.hh" #ifndef GRAPH_GSUBGPOS_GRAPH_HH #define GRAPH_GSUBGPOS_GRAPH_HH @@ -85,6 +87,12 @@ struct Lookup : public OT::Lookup return lookupType == extension_type (table_tag); } + bool use_mark_filtering_set () const + { + unsigned flag = lookupFlag; + return flag & 0x0010u; + } + bool make_extension (gsubgpos_graph_context_t& c, unsigned this_index) { @@ -120,22 +128,18 @@ struct Lookup : public OT::Lookup unsigned type = lookupType; bool is_ext = is_extension (c.table_tag); - if (c.table_tag != HB_OT_TAG_GPOS) + if (c.table_tag != HB_OT_TAG_GPOS && c.table_tag != HB_OT_TAG_GSUB) return true; - if (!is_ext && - type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair && - type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase) + if (!is_ext && !is_supported_gpos_type(type, c) && !is_supported_gsub_type(type, c)) 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; @@ -144,26 +148,47 @@ struct Lookup : public OT::Lookup 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) + if (!is_supported_gpos_type(type, c) && !is_supported_gsub_type(type, c)) continue; } - hb_vector_t new_sub_tables; - switch (type) + hb_vector_t* split_result; + if (c.split_subtables.has (subtable_index, &split_result)) { - 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 (split_result->length == 0) + continue; + all_new_subtables.push (hb_pair(i, *split_result)); + } + else + { + hb_vector_t new_sub_tables; + + if (c.table_tag == HB_OT_TAG_GPOS) { + switch (type) + { + case 2: + new_sub_tables = split_subtable (c, subtable_index); break; + case 4: + new_sub_tables = split_subtable (c, subtable_index); break; + default: + break; + } + } else if (c.table_tag == HB_OT_TAG_GSUB) { + switch (type) + { + case 4: + new_sub_tables = split_subtable (c, subtable_index); break; + default: + break; + } + } + + if (new_sub_tables.in_error ()) return false; + + c.split_subtables.set (subtable_index, new_sub_tables); + if (new_sub_tables) + all_new_subtables.push (hb_pair (i, std::move (new_sub_tables))); } - 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) { @@ -175,30 +200,29 @@ struct Lookup : public OT::Lookup 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); + return sub_table->split_subtables (c, objidx); } bool add_sub_tables (gsubgpos_graph_context_t& c, unsigned this_index, unsigned type, - hb_vector_t>>& subtable_ids) + const hb_vector_t>>& subtable_ids) { bool is_ext = is_extension (c.table_tag); - auto& v = c.graph.vertices_[this_index]; + 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 () + size_t new_size = v->table_size () + new_subtable_count * OT::Offset16::static_size; char* buffer = (char*) hb_calloc (1, new_size); if (!buffer) return false; @@ -207,10 +231,13 @@ struct Lookup : public OT::Lookup hb_free (buffer); return false; } - hb_memcpy (buffer, v.obj.head, v.table_size()); + hb_memcpy (buffer, v->obj.head, v->table_size()); - v.obj.head = buffer; - v.obj.tail = buffer + new_size; + if (use_mark_filtering_set ()) + hb_memcpy (buffer + new_size - 2, v->obj.tail - 2, 2); + + v->obj.head = buffer; + v->obj.tail = buffer + new_size; Lookup* new_lookup = (Lookup*) buffer; @@ -226,21 +253,23 @@ struct Lookup : public OT::Lookup if (is_ext) { unsigned ext_id = create_extension_subtable (c, subtable_id, type); - c.graph.vertices_[subtable_id].add_parent (ext_id); + c.graph.vertices_[subtable_id].add_parent (ext_id, false); subtable_id = ext_id; + // the reference to v may have changed on adding a node, so reassign it. + v = &c.graph.vertices_[this_index]; } - auto* link = v.obj.real_links.push (); + 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].add_parent (this_index); + c.graph.vertices_[subtable_id].add_parent (this_index, false); } } // Repacker sort order depends on link order, which we've messed up so resort it. - v.obj.real_links.qsort (); + v->obj.real_links.qsort (); // The head location of the lookup has changed, invalidating the lookups map entry // in the context. Update the map. @@ -250,17 +279,15 @@ struct Lookup : public OT::Lookup void fix_existing_subtable_links (gsubgpos_graph_context_t& c, unsigned this_index, - hb_vector_t>>& subtable_ids) + const 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; + unsigned insert_offset = Lookup::min_size + insert_index * OT::Offset16::static_size; shift += p.second.length; for (auto& l : v.obj.all_links_writer ()) @@ -326,7 +353,7 @@ struct Lookup : public OT::Lookup // Make extension point at the subtable. auto& ext_vertex = c.graph.vertices_[ext_index]; - ext_vertex.add_parent (lookup_index); + ext_vertex.add_parent (lookup_index, false); if (!existing_ext_index) subtable_vertex.remap_parent (lookup_index, ext_index); @@ -334,6 +361,19 @@ struct Lookup : public OT::Lookup } private: + bool is_supported_gsub_type(unsigned type, gsubgpos_graph_context_t& c) const { + return (c.table_tag == HB_OT_TAG_GSUB) && ( + type == OT::Layout::GSUB_impl::SubstLookupSubTable::Type::Ligature + ); + } + + bool is_supported_gpos_type(unsigned type, gsubgpos_graph_context_t& c) const { + return (c.table_tag == HB_OT_TAG_GPOS) && ( + type == OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair || + type == OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase + ); + } + unsigned extension_type (hb_tag_t table_tag) const { switch (table_tag) diff --git a/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh index fb4166128a9..6d1a3d4ae4b 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh @@ -212,7 +212,6 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2 split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { hb_set_t visited; @@ -265,7 +264,7 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2 split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { - switch (u.format) { + switch (u.format.v) { case 1: - return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); + return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, this_index); #ifndef HB_NO_BEYOND_64K case 2: HB_FALLTHROUGH; // Don't split 24bit MarkBasePos's. @@ -496,10 +494,10 @@ struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos 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; + if (vertex_len < u.format.v.get_size ()) return false; hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return ((MarkBasePosFormat1*)(&u.format1))->sanitize (vertex); #ifndef HB_NO_BEYOND_64K diff --git a/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh index fd46861de46..85950b63457 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh @@ -49,7 +49,6 @@ struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3 split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { hb_set_t visited; @@ -84,7 +83,7 @@ struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3 (split_context, split_points); @@ -207,7 +206,6 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4 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; @@ -291,7 +289,7 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4width = SmallTypes::size; class_def_link->objidx = class_def_2_id; class_def_link->position = 10; - graph.vertices_[class_def_2_id].add_parent (pair_pos_prime_id); + graph.vertices_[class_def_2_id].add_parent (pair_pos_prime_id, false); graph.duplicate (pair_pos_prime_id, class_def_2_id); return pair_pos_prime_id; @@ -607,14 +605,13 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4 split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { - switch (u.format) { + switch (u.format.v) { case 1: - return ((PairPosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); + return ((PairPosFormat1*)(&u.format1))->split_subtables (c, this_index); case 2: - return ((PairPosFormat2*)(&u.format2))->split_subtables (c, parent_index, this_index); + return ((PairPosFormat2*)(&u.format2))->split_subtables (c, this_index); #ifndef HB_NO_BEYOND_64K case 3: HB_FALLTHROUGH; case 4: HB_FALLTHROUGH; @@ -628,10 +625,10 @@ struct PairPos : public OT::Layout::GPOS_impl::PairPos 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; + if (vertex_len < u.format.v.get_size ()) return false; hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return ((PairPosFormat1*)(&u.format1))->sanitize (vertex); case 2: diff --git a/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh b/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh index 06e4bf44d8e..37fac8909e5 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh @@ -113,7 +113,7 @@ will_overflow (graph_t& graph, hb_hashmap_t record_set; const auto& vertices = graph.vertices_; - for (int parent_idx = vertices.length - 1; parent_idx >= 0; parent_idx--) + for (unsigned parent_idx : graph.ordering_) { // Don't need to check virtual links for overflow for (const auto& link : vertices.arrayZ[parent_idx].obj.real_links) @@ -172,14 +172,16 @@ void print_overflows (graph_t& graph, template inline void serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link, char* head, + unsigned size, + const hb_vector_t& id_map, hb_serialize_context_t* c) { + assert(link.position + link.width <= size); + OT::Offset* offset = reinterpret_cast*> (head + link.position); *offset = 0; c->add_link (*offset, - // serializer has an extra nil object at the start of the - // object array. So all id's are +1 of what our id's are. - link.objidx + 1, + id_map[link.objidx], (hb_serialize_context_t::whence_t) link.whence, link.bias); } @@ -187,6 +189,8 @@ serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link, inline void serialize_link (const hb_serialize_context_t::object_t::link_t& link, char* head, + unsigned size, + const hb_vector_t& id_map, hb_serialize_context_t* c) { switch (link.width) @@ -197,21 +201,21 @@ void serialize_link (const hb_serialize_context_t::object_t::link_t& link, case 4: if (link.is_signed) { - serialize_link_of_type (link, head, c); + serialize_link_of_type (link, head, size, id_map, c); } else { - serialize_link_of_type (link, head, c); + serialize_link_of_type (link, head, size, id_map, c); } return; case 2: if (link.is_signed) { - serialize_link_of_type (link, head, c); + serialize_link_of_type (link, head, size, id_map, c); } else { - serialize_link_of_type (link, head, c); + serialize_link_of_type (link, head, size, id_map, c); } return; case 3: - serialize_link_of_type (link, head, c); + serialize_link_of_type (link, head, size, id_map, c); return; default: // Unexpected link width. @@ -237,25 +241,36 @@ inline hb_blob_t* serialize (const graph_t& graph) c.start_serialize (); const auto& vertices = graph.vertices_; - for (unsigned i = 0; i < vertices.length; i++) { + + // Objects are placed in the serializer in reverse order since children need + // to be inserted before their parents. + + // Maps from our obj id's to the id's used during this serialization. + hb_vector_t id_map; + id_map.resize(graph.ordering_.length); + for (int pos = graph.ordering_.length - 1; pos >= 0; pos--) { + unsigned i = graph.ordering_[pos]; c.push (); - size_t size = vertices[i].obj.tail - vertices[i].obj.head; + auto& v = vertices[i]; + + size_t size = v.obj.tail - v.obj.head; + char* start = c.allocate_size (size); if (!start) { DEBUG_MSG (SUBSET_REPACK, nullptr, "Buffer out of space."); return nullptr; } - hb_memcpy (start, vertices[i].obj.head, size); + hb_memcpy (start, v.obj.head, size); // Only real links needs to be serialized. - for (const auto& link : vertices[i].obj.real_links) - serialize_link (link, start, &c); + for (const auto& link : v.obj.real_links) + serialize_link (link, start, size, id_map, &c); // All duplications are already encoded in the graph, so don't // enable sharing during packing. - c.pop_pack (false); + id_map[i] = c.pop_pack (false); } c.end_serialize (); diff --git a/src/java.desktop/share/native/libharfbuzz/graph/split-helpers.hh b/src/java.desktop/share/native/libharfbuzz/graph/split-helpers.hh index 61fd7c2d2ff..f1f86e332db 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/split-helpers.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/split-helpers.hh @@ -49,7 +49,7 @@ hb_vector_t actuate_subtable_split (Context& split_context, if (id == (unsigned) -1) { new_objects.reset (); - new_objects.allocated = -1; // mark error + new_objects.ensure_error (); return new_objects; } new_objects.push (id); @@ -58,7 +58,7 @@ hb_vector_t actuate_subtable_split (Context& split_context, if (!split_context.shrink (split_points[0])) { new_objects.reset (); - new_objects.allocated = -1; // mark error + new_objects.ensure_error (); } return new_objects; 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 d5a44bf473c..d2ce32616be 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 @@ -47,8 +47,7 @@ using namespace OT; struct ankr; -using hb_aat_class_cache_t = hb_cache_t<15, 8, 7>; -static_assert (sizeof (hb_aat_class_cache_t) == 256, ""); +using hb_aat_class_cache_t = hb_ot_layout_mapping_cache_t; struct hb_aat_scratch_t { @@ -79,7 +78,10 @@ struct hb_aat_scratch_t { hb_bit_set_t *s = buffer_glyph_set.get_acquire (); if (s && buffer_glyph_set.cmpexch (s, nullptr)) + { + s->clear (); return s; + } s = (hb_bit_set_t *) hb_calloc (1, sizeof (hb_bit_set_t)); if (unlikely (!s)) @@ -124,13 +126,14 @@ struct hb_aat_apply_context_t : const OT::GDEF &gdef; bool has_glyph_classes; const hb_sorted_vector_t *range_flags = nullptr; + hb_mask_t subtable_flags = 0; + bool buffer_is_reversed = false; + // Caches bool using_buffer_glyph_set = false; hb_bit_set_t *buffer_glyph_set = nullptr; - const hb_bit_set_t *left_set = nullptr; - const hb_bit_set_t *right_set = nullptr; - const hb_bit_set_t *machine_glyph_set = nullptr; + const hb_bit_set_t *first_set = nullptr; + const hb_bit_set_t *second_set = nullptr; hb_aat_class_cache_t *machine_class_cache = nullptr; - hb_mask_t subtable_flags = 0; /* Unused. For debug tracing only. */ unsigned int lookup_index; @@ -146,6 +149,12 @@ struct hb_aat_apply_context_t : void set_lookup_index (unsigned int i) { lookup_index = i; } + void reverse_buffer () + { + buffer->reverse (); + buffer_is_reversed = !buffer_is_reversed; + } + void setup_buffer_glyph_set () { using_buffer_glyph_set = buffer->len >= 4 && buffer_glyph_set; @@ -156,11 +165,11 @@ struct hb_aat_apply_context_t : bool buffer_intersects_machine () const { if (likely (using_buffer_glyph_set)) - return buffer_glyph_set->intersects (*machine_glyph_set); + return buffer_glyph_set->intersects (*first_set); // Faster for shorter buffers. for (unsigned i = 0; i < buffer->len; i++) - if (machine_glyph_set->has (buffer->info[i].codepoint)) + if (first_set->has (buffer->info[i].codepoint)) return true; return false; } @@ -639,6 +648,23 @@ struct LookupFormat10 glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1); } + template + void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const + { + if (unlikely (!glyphCount)) return; + if (firstGlyph == DELETED_GLYPH) return; + const HBUINT8 *p = valueArrayZ.arrayZ; + for (unsigned i = 0; i < glyphCount; i++) + { + unsigned int v = 0; + unsigned int count = valueSize; + for (unsigned int j = 0; j < count; j++) + v = (v << 8) | *p++; + if (filter (v)) + glyphs.add (firstGlyph + i); + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -666,7 +692,7 @@ struct Lookup { const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0.get_value (glyph_id, num_glyphs); case 2: hb_barrier (); return u.format2.get_value (glyph_id); case 4: hb_barrier (); return u.format4.get_value (glyph_id); @@ -678,7 +704,7 @@ struct Lookup const typename T::type get_value_or_null (hb_codepoint_t glyph_id, unsigned int num_glyphs) const { - switch (u.format) { + switch (u.format.v) { /* Format 10 cannot return a pointer. */ case 10: hb_barrier (); return u.format10.get_value_or_null (glyph_id); default: @@ -690,7 +716,7 @@ struct Lookup template void collect_glyphs (set_t &glyphs, unsigned int num_glyphs) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); u.format0.collect_glyphs (glyphs, num_glyphs); return; case 2: hb_barrier (); u.format2.collect_glyphs (glyphs); return; case 4: hb_barrier (); u.format4.collect_glyphs (glyphs); return; @@ -703,12 +729,13 @@ struct Lookup template void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); u.format0.collect_glyphs_filtered (glyphs, num_glyphs, filter); return; case 2: hb_barrier (); u.format2.collect_glyphs_filtered (glyphs, filter); return; case 4: hb_barrier (); u.format4.collect_glyphs_filtered (glyphs, filter); return; case 6: hb_barrier (); u.format6.collect_glyphs_filtered (glyphs, filter); return; case 8: hb_barrier (); u.format8.collect_glyphs_filtered (glyphs, filter); return; + case 10: hb_barrier (); u.format10.collect_glyphs_filtered (glyphs, filter); return; default:return; } } @@ -724,9 +751,9 @@ struct Lookup bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return_trace (u.format0.sanitize (c)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); case 4: hb_barrier (); return_trace (u.format4.sanitize (c)); @@ -739,9 +766,9 @@ struct Lookup bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return_trace (u.format0.sanitize (c, base)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c, base)); case 4: hb_barrier (); return_trace (u.format4.sanitize (c, base)); @@ -754,7 +781,7 @@ struct Lookup protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ LookupFormat0 format0; LookupFormat2 format2; LookupFormat4 format4; @@ -763,7 +790,7 @@ struct Lookup LookupFormat10 format10; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2); @@ -838,11 +865,6 @@ struct StateTable STATE_START_OF_LINE = 1, }; - template - void collect_glyphs (set_t &glyphs, unsigned num_glyphs) const - { - (this+classTable).collect_glyphs (glyphs, num_glyphs); - } template void collect_initial_glyphs (set_t &glyphs, unsigned num_glyphs, const table_t &table) const { @@ -1082,6 +1104,8 @@ struct SubtableGlyphCoverage for (unsigned i = 0; i < subtable_count; i++) { uint32_t offset = (uint32_t) subtableOffsets[i]; + // A font file called SFNSDisplay.ttf has value 0xFFFFFFFF in the offsets. + // Just ignore it. if (offset == 0 || offset == 0xFFFFFFFF) continue; if (unlikely (!subtableOffsets[i].sanitize (c, this, bytes))) @@ -1192,11 +1216,24 @@ struct StateTableDriver 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; + const bool start_state_safe_to_break_eot = + !c->table->is_actionable (machine.get_entry (StateTableT::STATE_START_OF_TEXT, CLASS_END_OF_TEXT)); for (buffer->idx = 0; buffer->successful;) { - /* This block is copied in NoncontextualSubtable::apply. Keep in sync. */ - if (last_range) + unsigned int klass = likely (buffer->idx < buffer->len) ? + machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_class_cache) : + (unsigned) CLASS_END_OF_TEXT; + resume: + DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx); + const EntryT &entry = machine.get_entry (state, klass); + const int next_state = machine.new_state (entry.newState); + + bool is_not_epsilon_transition = !(entry.flags & Flags::DontAdvance); + bool is_not_actionable = !c->table->is_actionable (entry); + + if (unlikely (last_range)) { + /* This block is copied in NoncontextualSubtable::apply. Keep in sync. */ auto *range = last_range; if (buffer->idx < buffer->len) { @@ -1211,7 +1248,7 @@ struct StateTableDriver } if (!(range->flags & ac->subtable_flags)) { - if (buffer->idx == buffer->len || unlikely (!buffer->successful)) + if (buffer->idx == buffer->len) break; state = StateTableT::STATE_START_OF_TEXT; @@ -1219,13 +1256,42 @@ struct StateTableDriver continue; } } + else + { + // Fast path for when transitioning from start-state to start-state with + // no action and advancing. Do so as long as the class remains the same. + // This is common with runs of non-actionable glyphs. - unsigned int klass = likely (buffer->idx < buffer->len) ? - machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_class_cache) : - (unsigned) CLASS_END_OF_TEXT; - DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx); - const EntryT &entry = machine.get_entry (state, klass); - const int next_state = machine.new_state (entry.newState); + bool is_null_transition = state == StateTableT::STATE_START_OF_TEXT && + next_state == StateTableT::STATE_START_OF_TEXT && + start_state_safe_to_break_eot && + is_not_actionable && + is_not_epsilon_transition && + !last_range; + + if (is_null_transition) + { + unsigned old_klass = klass; + do + { + c->transition (buffer, this, entry); + + if (buffer->idx == buffer->len || !buffer->successful) + break; + + (void) buffer->next_glyph (); + + klass = likely (buffer->idx < buffer->len) ? + machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_class_cache) : + (unsigned) CLASS_END_OF_TEXT; + } while (klass == old_klass); + + if (buffer->idx == buffer->len || !buffer->successful) + break; + + goto resume; + } + } /* Conditions under which it's guaranteed safe-to-break before current glyph: * @@ -1292,10 +1358,10 @@ struct StateTableDriver state = next_state; DEBUG_MSG (APPLY, nullptr, "s%d", state); - if (buffer->idx == buffer->len || unlikely (!buffer->successful)) + if (buffer->idx == buffer->len) break; - if (!(entry.flags & Flags::DontAdvance) || buffer->max_ops-- <= 0) + if (is_not_epsilon_transition || buffer->max_ops-- <= 0) (void) buffer->next_glyph (); } 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 ca4f2243346..2a6b813972f 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 @@ -120,12 +120,12 @@ struct KerxSubTableFormat0 } template - void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const + void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const { for (const KernPair& pair : pairs) { - left_set.add (pair.left); - right_set.add (pair.right); + first_set.add (pair.left); + second_set.add (pair.right); } } @@ -140,7 +140,7 @@ struct KerxSubTableFormat0 int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const { - if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0; + if (!(*c->first_set)[left] || !(*c->second_set)[right]) return 0; return table.get_kerning (left, right, c); } }; @@ -396,10 +396,10 @@ struct KerxSubTableFormat1 } template - void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const + void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const { - machine.collect_initial_glyphs (left_set, num_glyphs, *this); - //machine.collect_glyphs (right_set, num_glyphs); // right_set is unused for machine kerning + machine.collect_initial_glyphs (first_set, num_glyphs, *this); + //machine.collect_glyphs (second_set, num_glyphs); // second_set is unused for machine kerning } protected: @@ -451,10 +451,10 @@ struct KerxSubTableFormat2 } template - void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const + void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const { - (this+leftClassTable).collect_glyphs (left_set, num_glyphs); - (this+rightClassTable).collect_glyphs (right_set, num_glyphs); + (this+leftClassTable).collect_glyphs (first_set, num_glyphs); + (this+rightClassTable).collect_glyphs (second_set, num_glyphs); } struct accelerator_t @@ -468,7 +468,7 @@ struct KerxSubTableFormat2 int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const { - if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0; + if (!(*c->first_set)[left] || !(*c->second_set)[right]) return 0; return table.get_kerning (left, right, c); } }; @@ -629,6 +629,8 @@ struct KerxSubTableFormat4 } o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_MARK; o.attach_chain() = (int) mark - (int) buffer->idx; + if (c->buffer_is_reversed) + o.attach_chain() = -o.attach_chain(); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; } @@ -671,10 +673,10 @@ struct KerxSubTableFormat4 } template - void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const + void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const { - machine.collect_initial_glyphs (left_set, num_glyphs, *this); - //machine.collect_glyphs (right_set, num_glyphs); // right_set is unused for machine kerning + machine.collect_initial_glyphs (first_set, num_glyphs, *this); + //machine.collect_glyphs (second_set, num_glyphs); // second_set is unused for machine kerning } protected: @@ -762,19 +764,19 @@ struct KerxSubTableFormat6 } template - void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const + void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const { if (is_long ()) { const auto &t = u.l; - (this+t.rowIndexTable).collect_glyphs (left_set, num_glyphs); - (this+t.columnIndexTable).collect_glyphs (right_set, num_glyphs); + (this+t.rowIndexTable).collect_glyphs (first_set, num_glyphs); + (this+t.columnIndexTable).collect_glyphs (second_set, num_glyphs); } else { const auto &t = u.s; - (this+t.rowIndexTable).collect_glyphs (left_set, num_glyphs); - (this+t.columnIndexTable).collect_glyphs (right_set, num_glyphs); + (this+t.rowIndexTable).collect_glyphs (first_set, num_glyphs); + (this+t.columnIndexTable).collect_glyphs (second_set, num_glyphs); } } @@ -789,7 +791,7 @@ struct KerxSubTableFormat6 int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const { - if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0; + if (!(*c->first_set)[left] || !(*c->second_set)[right]) return 0; return table.get_kerning (left, right, c); } }; @@ -878,15 +880,15 @@ struct KerxSubTable } template - void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const + void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const { unsigned int subtable_type = get_type (); switch (subtable_type) { - case 0: u.format0.collect_glyphs (left_set, right_set, num_glyphs); return; - case 1: u.format1.collect_glyphs (left_set, right_set, num_glyphs); return; - case 2: u.format2.collect_glyphs (left_set, right_set, num_glyphs); return; - case 4: u.format4.collect_glyphs (left_set, right_set, num_glyphs); return; - case 6: u.format6.collect_glyphs (left_set, right_set, num_glyphs); return; + case 0: u.format0.collect_glyphs (first_set, second_set, num_glyphs); return; + case 1: u.format1.collect_glyphs (first_set, second_set, num_glyphs); return; + case 2: u.format2.collect_glyphs (first_set, second_set, num_glyphs); return; + case 4: u.format4.collect_glyphs (first_set, second_set, num_glyphs); return; + case 6: u.format6.collect_glyphs (first_set, second_set, num_glyphs); return; default: return; } } @@ -923,8 +925,8 @@ struct KerxSubTable struct kern_subtable_accelerator_data_t { - hb_bit_set_t left_set; - hb_bit_set_t right_set; + hb_bit_set_t first_set; + hb_bit_set_t second_set; mutable hb_aat_class_cache_t class_cache; }; @@ -1017,9 +1019,8 @@ struct KerxTable if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ()) goto skip; - c->left_set = &subtable_accel.left_set; - c->right_set = &subtable_accel.right_set; - c->machine_glyph_set = &subtable_accel.left_set; + c->first_set = &subtable_accel.first_set; + c->second_set = &subtable_accel.second_set; c->machine_class_cache = &subtable_accel.class_cache; if (!c->buffer_intersects_machine ()) @@ -1051,8 +1052,8 @@ struct KerxTable } } - if (reverse) - c->buffer->reverse (); + if (reverse != c->buffer_is_reversed) + c->reverse_buffer (); { /* See comment in sanitize() for conditional here. */ @@ -1060,15 +1061,14 @@ struct KerxTable ret |= st->dispatch (c); } - if (reverse) - c->buffer->reverse (); - (void) c->buffer->message (c->font, "end subtable %u", c->lookup_index); skip: st = &StructAfter (*st); c->set_lookup_index (c->lookup_index + 1); } + if (c->buffer_is_reversed) + c->reverse_buffer (); return ret; } @@ -1133,7 +1133,7 @@ struct KerxTable if (unlikely (accel_data.subtable_accels.in_error ())) return accel_data; - st->collect_glyphs (subtable_accel.left_set, subtable_accel.right_set, num_glyphs); + st->collect_glyphs (subtable_accel.first_set, subtable_accel.second_set, num_glyphs); subtable_accel.class_cache.clear (); 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 92b07eec6e5..2cbb86c7566 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 @@ -487,7 +487,7 @@ struct LigatureSubtable if (entry.flags & LigatureEntryT::SetComponent) { /* Never mark same index twice, in case DontAdvance was used... */ - if (match_length && match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] == buffer->out_len) + if (unlikely (match_length && match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] == buffer->out_len)) match_length--; match_positions[match_length++ % ARRAY_LENGTH (match_positions)] = buffer->out_len; @@ -640,7 +640,7 @@ struct NoncontextualSubtable for (unsigned int i = 0; i < count; i++) { /* This block copied from StateTableDriver::drive. Keep in sync. */ - if (last_range) + if (unlikely (last_range)) { auto *range = last_range; { @@ -1169,15 +1169,15 @@ struct Chain hb_map ([subtable_flags] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable_flags & (_.flags); }))) goto skip; - c->subtable_flags = subtable_flags; - c->machine_glyph_set = accel ? &accel->subtables[i].glyph_set : &Null(hb_bit_set_t); - c->machine_class_cache = accel ? &accel->subtables[i].class_cache : nullptr; - if (!(coverage & ChainSubtable::AllDirections) && HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != bool (coverage & ChainSubtable::Vertical)) goto skip; + c->subtable_flags = subtable_flags; + c->first_set = accel ? &accel->subtables[i].glyph_set : &Null(hb_bit_set_t); + c->machine_class_cache = accel ? &accel->subtables[i].class_cache : nullptr; + if (!c->buffer_intersects_machine ()) { (void) c->buffer->message (c->font, "skipped chainsubtable %u because no glyph matches", c->lookup_index); @@ -1219,22 +1219,21 @@ struct Chain if (!c->buffer->message (c->font, "start chainsubtable %u", c->lookup_index)) goto skip; - if (reverse) - c->buffer->reverse (); + if (reverse != c->buffer_is_reversed) + c->reverse_buffer (); subtable->apply (c); - if (reverse) - c->buffer->reverse (); - (void) c->buffer->message (c->font, "end chainsubtable %u", c->lookup_index); - if (unlikely (!c->buffer->successful)) return; + if (unlikely (!c->buffer->successful)) break; skip: subtable = &StructAfter> (*subtable); c->set_lookup_index (c->lookup_index + 1); } + if (c->buffer_is_reversed) + c->reverse_buffer (); } unsigned int get_size () const { return length; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-algs.hh b/src/java.desktop/share/native/libharfbuzz/hb-algs.hh index 6e250bfdef0..651ffcda01d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-algs.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-algs.hh @@ -78,129 +78,246 @@ /* - * Big-endian integers. + * Fixed-endian integers / floats. */ + /* Endian swap, used in Windows related backends */ static inline constexpr uint16_t hb_uint16_swap (uint16_t v) { return (v >> 8) | (v << 8); } static inline constexpr uint32_t hb_uint32_swap (uint32_t v) { return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); } -#ifndef HB_FAST_INT_ACCESS +template +struct __attribute__((packed)) hb_packed_t { Type v; }; + +#ifndef HB_FAST_NUM_ACCESS + #if defined(__OPTIMIZE__) && \ defined(__BYTE_ORDER) && \ (__BYTE_ORDER == __BIG_ENDIAN || \ (__BYTE_ORDER == __LITTLE_ENDIAN && \ hb_has_builtin(__builtin_bswap16) && \ - hb_has_builtin(__builtin_bswap32))) -#define HB_FAST_INT_ACCESS 1 + hb_has_builtin(__builtin_bswap32) && \ + hb_has_builtin(__builtin_bswap64))) +#define HB_FAST_NUM_ACCESS 1 #else -#define HB_FAST_INT_ACCESS 0 -#endif +#define HB_FAST_NUM_ACCESS 0 #endif -template -struct BEInt; -template -struct BEInt +// https://github.com/harfbuzz/harfbuzz/issues/5456 +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ <= 12) +#undef HB_FAST_NUM_ACCESS +#define HB_FAST_NUM_ACCESS 0 +#endif + +#endif + +template +struct HBInt; +template +struct HBInt { public: - BEInt () = default; - constexpr BEInt (Type V) : v {uint8_t (V)} {} + HBInt () = default; + constexpr HBInt (Type V) : v {uint8_t (V)} {} constexpr operator Type () const { return v; } private: uint8_t v; }; -template -struct BEInt +template +struct HBInt { - struct __attribute__((packed)) packed_uint16_t { uint16_t v; }; - public: - BEInt () = default; + HBInt () = default; - BEInt (Type V) -#if HB_FAST_INT_ACCESS -#if __BYTE_ORDER == __LITTLE_ENDIAN - { ((packed_uint16_t *) v)->v = __builtin_bswap16 (V); } -#else /* __BYTE_ORDER == __BIG_ENDIAN */ - { ((packed_uint16_t *) v)->v = V; } -#endif + HBInt (Type V) +#if HB_FAST_NUM_ACCESS + { + if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) + ((hb_packed_t *) v)->v = V; + else + ((hb_packed_t *) v)->v = __builtin_bswap16 (V); + } #else - : v {uint8_t ((V >> 8) & 0xFF), - uint8_t ((V ) & 0xFF)} {} + : v {BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V ) & 0xFF), + BE ? uint8_t ((V ) & 0xFF) : uint8_t ((V >> 8) & 0xFF)} {} #endif - constexpr operator Type () const { -#if HB_FAST_INT_ACCESS -#if __BYTE_ORDER == __LITTLE_ENDIAN - return __builtin_bswap16 (((packed_uint16_t *) v)->v); -#else /* __BYTE_ORDER == __BIG_ENDIAN */ - return ((packed_uint16_t *) v)->v; -#endif + constexpr operator Type () const + { +#if HB_FAST_NUM_ACCESS + return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ? + ((const hb_packed_t *) v)->v + : + __builtin_bswap16 (((const hb_packed_t *) v)->v) + ; #else - return (v[0] << 8) - + (v[1] ); + return (BE ? (v[0] << 8) : (v[0] )) + + (BE ? (v[1] ) : (v[1] << 8)); #endif } private: uint8_t v[2]; }; -template -struct BEInt +template +struct HBInt { static_assert (!std::is_signed::value, ""); public: - BEInt () = default; - constexpr BEInt (Type V) : v {uint8_t ((V >> 16) & 0xFF), - uint8_t ((V >> 8) & 0xFF), - uint8_t ((V ) & 0xFF)} {} + HBInt () = default; + constexpr HBInt (Type V) : v {BE ? uint8_t ((V >> 16) & 0xFF) : uint8_t ((V >> 16) & 0xFF), + BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V >> 8) & 0xFF), + BE ? uint8_t ((V ) & 0xFF) : uint8_t ((V ) & 0xFF)} {} - constexpr operator Type () const { return (v[0] << 16) - + (v[1] << 8) - + (v[2] ); } + constexpr operator Type () const { return (BE ? (v[0] << 16) : (v[0] )) + + (BE ? (v[1] << 8) : (v[1] << 8)) + + (BE ? (v[2] ) : (v[2] << 16)); } private: uint8_t v[3]; }; -template -struct BEInt +template +struct HBInt { - struct __attribute__((packed)) packed_uint32_t { uint32_t v; }; + template + friend struct HBFloat; public: - BEInt () = default; + HBInt () = default; - BEInt (Type V) -#if HB_FAST_INT_ACCESS -#if __BYTE_ORDER == __LITTLE_ENDIAN - { ((packed_uint32_t *) v)->v = __builtin_bswap32 (V); } -#else /* __BYTE_ORDER == __BIG_ENDIAN */ - { ((packed_uint32_t *) v)->v = V; } -#endif + HBInt (Type V) +#if HB_FAST_NUM_ACCESS + { + if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) + ((hb_packed_t *) v)->v = V; + else + ((hb_packed_t *) v)->v = __builtin_bswap32 (V); + } #else - : v {uint8_t ((V >> 24) & 0xFF), - uint8_t ((V >> 16) & 0xFF), - uint8_t ((V >> 8) & 0xFF), - uint8_t ((V ) & 0xFF)} {} + : v {BE ? uint8_t ((V >> 24) & 0xFF) : uint8_t ((V ) & 0xFF), + BE ? uint8_t ((V >> 16) & 0xFF) : uint8_t ((V >> 8) & 0xFF), + BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V >> 16) & 0xFF), + BE ? uint8_t ((V ) & 0xFF) : uint8_t ((V >> 24) & 0xFF)} {} #endif constexpr operator Type () const { -#if HB_FAST_INT_ACCESS -#if __BYTE_ORDER == __LITTLE_ENDIAN - return __builtin_bswap32 (((packed_uint32_t *) v)->v); -#else /* __BYTE_ORDER == __BIG_ENDIAN */ - return ((packed_uint32_t *) v)->v; -#endif +#if HB_FAST_NUM_ACCESS + return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ? + ((const hb_packed_t *) v)->v + : + __builtin_bswap32 (((const hb_packed_t *) v)->v) + ; #else - return (v[0] << 24) - + (v[1] << 16) - + (v[2] << 8) - + (v[3] ); + return (BE ? (v[0] << 24) : (v[0] )) + + (BE ? (v[1] << 16) : (v[1] << 8)) + + (BE ? (v[2] << 8) : (v[2] << 16)) + + (BE ? (v[3] ) : (v[3] << 24)); #endif } private: uint8_t v[4]; }; +template +struct HBInt +{ + template + friend struct HBFloat; + + public: + HBInt () = default; + + HBInt (Type V) +#if HB_FAST_NUM_ACCESS + { + if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) + ((hb_packed_t *) v)->v = V; + else + ((hb_packed_t *) v)->v = __builtin_bswap64 (V); + } +#else + : v {BE ? uint8_t ((V >> 56) & 0xFF) : uint8_t ((V ) & 0xFF), + BE ? uint8_t ((V >> 48) & 0xFF) : uint8_t ((V >> 8) & 0xFF), + BE ? uint8_t ((V >> 40) & 0xFF) : uint8_t ((V >> 16) & 0xFF), + BE ? uint8_t ((V >> 32) & 0xFF) : uint8_t ((V >> 24) & 0xFF), + BE ? uint8_t ((V >> 24) & 0xFF) : uint8_t ((V >> 32) & 0xFF), + BE ? uint8_t ((V >> 16) & 0xFF) : uint8_t ((V >> 40) & 0xFF), + BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V >> 48) & 0xFF), + BE ? uint8_t ((V ) & 0xFF) : uint8_t ((V >> 56) & 0xFF)} {} +#endif + + constexpr operator Type () const { +#if HB_FAST_NUM_ACCESS + return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ? + ((const hb_packed_t *) v)->v + : + __builtin_bswap64 (((const hb_packed_t *) v)->v) + ; +#else + return (BE ? (uint64_t (v[0]) << 56) : (uint64_t (v[0]) )) + + (BE ? (uint64_t (v[1]) << 48) : (uint64_t (v[1]) << 8)) + + (BE ? (uint64_t (v[2]) << 40) : (uint64_t (v[2]) << 16)) + + (BE ? (uint64_t (v[3]) << 32) : (uint64_t (v[3]) << 24)) + + (BE ? (uint64_t (v[4]) << 24) : (uint64_t (v[4]) << 32)) + + (BE ? (uint64_t (v[5]) << 16) : (uint64_t (v[5]) << 40)) + + (BE ? (uint64_t (v[6]) << 8) : (uint64_t (v[6]) << 48)) + + (BE ? (uint64_t (v[7]) ) : (uint64_t (v[7]) << 56)); +#endif + } + private: uint8_t v[8]; +}; /* Floats. */ +template +struct HBFloat +{ + using IntType = typename std::conditional::type; + + public: + HBFloat () = default; + + HBFloat (Type V) + { +#if HB_FAST_NUM_ACCESS + { + if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) + { + ((hb_packed_t *) v)->v = V; + return; + } + } +#endif + + union { + hb_packed_t f; + hb_packed_t i; + } u = {{V}}; + + const HBInt I = u.i.v; + for (unsigned i = 0; i < Bytes; i++) + v[i] = I.v[i]; + } + + /* c++14 constexpr */ operator Type () const + { +#if HB_FAST_NUM_ACCESS + { + if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) + return ((const hb_packed_t *) v)->v; + } +#endif + + HBInt I; + for (unsigned i = 0; i < Bytes; i++) + I.v[i] = v[i]; + + union { + hb_packed_t i; + hb_packed_t f; + } u = {{I}}; + + return u.f.v; + } + private: uint8_t v[Bytes]; +}; + + /* We want our rounding towards +infinity. */ static inline double _hb_roundf (double x) { return floor (x + .5); } @@ -210,6 +327,27 @@ _hb_roundf (float x) { return floorf (x + .5f); } #define roundf(x) _hb_roundf(x) +static inline void +hb_sincos (float rotation, float &s, float &c) +{ +#ifdef HAVE_SINCOSF + sincosf (rotation, &s, &c); +#else + c = cosf (rotation); + s = sinf (rotation); +#endif +} +static inline void +hb_sincos (double rotation, double &s, double &c) +{ +#ifdef HAVE_SINCOS + sincos (rotation, &s, &c); +#else + c = cos (rotation); + s = sin (rotation); +#endif +} + /* Encodes three unsigned integers in one 64-bit number. If the inputs have more than 21 bits, * values will be truncated / overlap, and might not decode exactly. */ @@ -734,6 +872,17 @@ HB_FUNCOBJ (hb_clamp); * Bithacks. */ +/* Return the number of 1 bits in a uint8_t; faster than hb_popcount() */ +static inline unsigned +hb_popcount8 (uint8_t v) +{ + static const uint8_t popcount4[16] = { + 0, 1, 1, 2, 1, 2, 2, 3, + 1, 2, 2, 3, 2, 3, 3, 4 + }; + return popcount4[v & 0xF] + popcount4[v >> 4]; +} + /* Return the number of 1 bits in v. */ template static inline unsigned int @@ -1070,6 +1219,7 @@ _hb_cmp_operator (const void *pkey, const void *pval) } template +HB_HOT static inline bool hb_bsearch_impl (unsigned *pos, /* Out */ const K& key, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-alloc-pool.hh b/src/java.desktop/share/native/libharfbuzz/hb-alloc-pool.hh new file mode 100644 index 00000000000..cb0d7aa7acd --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-alloc-pool.hh @@ -0,0 +1,105 @@ +/* + * 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. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_ALLOC_POOL_HH +#define HB_ALLOC_POOL_HH + +#include "hb-vector.hh" + +/* Memory pool for persistent small- to medium-sized allocations. + * + * Some AI musings on this, not necessarily true: + * + * This is a very simple implementation, but it's good enough for our + * purposes. It's not thread-safe. It's not very fast. It's not + * very memory efficient. It's not very cache efficient. It's not + * very anything efficient. But it's simple and it works. And it's + * good enough for our purposes. If you need something more + * sophisticated, use a real allocator. Or use a real language. */ + +struct hb_alloc_pool_t +{ + unsigned ChunkSize = 65536 - 2 * sizeof (void *); + + void *alloc (size_t size, unsigned alignment = 2 * sizeof (void *)) + { + if (unlikely (chunks.in_error ())) return nullptr; + + assert (alignment > 0); + assert (alignment <= 2 * sizeof (void *)); + assert ((alignment & (alignment - 1)) == 0); /* power of two */ + + if (size > (ChunkSize) / 4) + { + /* Big chunk, allocate separately. */ + hb_vector_t chunk; + if (unlikely (!chunk.resize (size))) return nullptr; + void *ret = chunk.arrayZ; + chunks.push (std::move (chunk)); + if (chunks.in_error ()) return nullptr; + if (chunks.length > 1) + { + // Bring back the previous last chunk to the end, so that + // we can continue to allocate from it. + hb_swap (chunks.arrayZ[chunks.length - 1], chunks.arrayZ[chunks.length - 2]); + } + return ret; + } + + unsigned pad = (unsigned) ((alignment - ((uintptr_t) current_chunk.arrayZ & (alignment - 1))) & (alignment - 1)); + + // Small chunk, allocate from the last chunk. + if (current_chunk.length < pad + size) + { + chunks.push (); + if (unlikely (chunks.in_error ())) return nullptr; + hb_vector_t &chunk = chunks.arrayZ[chunks.length - 1]; + if (unlikely (!chunk.resize (ChunkSize))) return nullptr; + current_chunk = chunk; + pad = (unsigned) ((alignment - ((uintptr_t) current_chunk.arrayZ & (alignment - 1))) & (alignment - 1)); + } + + current_chunk += pad; + + assert (current_chunk.length >= size); + void *ret = current_chunk.arrayZ; + current_chunk += size; + return ret; + } + + void discard (void *p_, size_t size) + { + // Reclaim memory if we can. + char *p = (char *) p_; + if (current_chunk.arrayZ == p + size && current_chunk.backwards_length >= size) + current_chunk -= size; + } + + private: + hb_vector_t> chunks; + hb_array_t current_chunk; +}; + + +#endif /* HB_ALLOC_POOL_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-array.hh b/src/java.desktop/share/native/libharfbuzz/hb-array.hh index d65bbc5c711..71d3bcdf1ea 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-array.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-array.hh @@ -291,6 +291,13 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&> && (unsigned int) (arrayZ + length - (const char *) p) >= size; } + template + bool check_end (const void *p) const + { + return (uintptr_t) (((const char *) p) - arrayZ) <= length; + } + /* Only call if you allocated the underlying array using hb_malloc() or similar. */ void fini () { hb_free ((void *) arrayZ); arrayZ = nullptr; length = 0; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh index 80bd90e87dc..05aaf5a155e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh @@ -40,7 +40,6 @@ * Atomic integers and pointers. */ - /* We need external help for these */ #if defined(hb_atomic_int_impl_add) \ @@ -80,27 +79,11 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) #include +#define HB_STL_ATOMIC_IMPL + #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::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)) -#define hb_atomic_ptr_impl_get(P) (reinterpret_cast *> (P)->load (std::memory_order_acquire)) -static inline bool -_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) -{ - const void *O = O_; // Need lvalue - return reinterpret_cast *> (P)->compare_exchange_weak (O, N, std::memory_order_acq_rel, std::memory_order_relaxed); -} -#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N)) - - #else /* defined(HB_NO_MT) */ #define hb_atomic_int_impl_add(AI, V) ((*(AI) += (V)) - (V)) @@ -159,6 +142,76 @@ inline T hb_atomic_int_impl_get (const T *AI) { T v = *AI; _hb_memory_r_barrie inline void *hb_atomic_ptr_impl_get (void ** const P) { void *v = *P; _hb_memory_r_barrier (); return v; } #endif +#ifdef HB_STL_ATOMIC_IMPL +template +struct hb_atomic_t +{ + hb_atomic_t () = default; + constexpr hb_atomic_t (T v) : v (v) {} + constexpr hb_atomic_t (const hb_atomic_t& o) : v (o.get_relaxed ()) {} + constexpr hb_atomic_t (hb_atomic_t&& o) : v (o.get_relaxed ()) { o.set_relaxed ({}); } + + hb_atomic_t &operator= (const hb_atomic_t& o) { set_relaxed (o.get_relaxed ()); return *this; } + hb_atomic_t &operator= (hb_atomic_t&& o){ set_relaxed (o.get_relaxed ()); o.set_relaxed ({}); return *this; } + hb_atomic_t &operator= (T v_) + { + set_relaxed (v_); + return *this; + } + operator T () const { return get_relaxed (); } + + void set_relaxed (T v_) { v.store (v_, std::memory_order_relaxed); } + void set_release (T v_) { v.store (v_, std::memory_order_release); } + T get_relaxed () const { return v.load (std::memory_order_relaxed); } + T get_acquire () const { return v.load (std::memory_order_acquire); } + T inc () { return v.fetch_add (1, std::memory_order_acq_rel); } + T dec () { return v.fetch_add (-1, std::memory_order_acq_rel); } + + int operator++ (int) { return inc (); } + int operator-- (int) { return dec (); } + + friend void swap (hb_atomic_t &a, hb_atomic_t &b) noexcept + { + T v = a.get_acquire (); + a.set_relaxed (b.get_acquire ()); + b.set_relaxed (v); + } + + std::atomic v = 0; +}; + +template +struct hb_atomic_t +{ + hb_atomic_t () = default; + constexpr hb_atomic_t (T *v) : v (v) {} + hb_atomic_t (const hb_atomic_t &other) = delete; + + void init (T *v_ = nullptr) { set_relaxed (v_); } + void set_relaxed (T *v_) { v.store (v_, std::memory_order_relaxed); } + T *get_relaxed () const { return v.load (std::memory_order_relaxed); } + T *get_acquire () const { return v.load (std::memory_order_acquire); } + bool cmpexch (T *old, T *new_) { return v.compare_exchange_weak (old, new_, std::memory_order_acq_rel, std::memory_order_relaxed); } + + operator bool () const { return get_acquire () != nullptr; } + T *operator->() const { return get_acquire (); } + template + operator C * () const + { + return get_acquire (); + } + + friend void swap (hb_atomic_t &a, hb_atomic_t &b) noexcept + { + T *p = a.get_acquire (); + a.set_relaxed (b.get_acquire ()); + b.set_relaxed (p); + } + + std::atomic v = nullptr; +}; + +#else template struct hb_atomic_t @@ -178,7 +231,6 @@ struct hb_atomic_t int operator ++ (int) { return inc (); } int operator -- (int) { return dec (); } - long operator |= (long v_) { set_relaxed (get_relaxed () | v_); return *this; } T v = 0; }; @@ -194,7 +246,7 @@ struct hb_atomic_t 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_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); } - bool cmpexch (const T *old, T *new_) { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); } + bool cmpexch (T *old, T *new_) { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); } operator bool () const { return get_acquire () != nullptr; } T * operator -> () const { return get_acquire (); } @@ -203,6 +255,8 @@ struct hb_atomic_t T *v = nullptr; }; +#endif + static inline bool hb_barrier () { _hb_compiler_memory_r_barrier (); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh b/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh index f541472544a..f9c0e8870ff 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh @@ -176,7 +176,7 @@ struct hb_inc_bimap_t { hb_codepoint_t count = get_population (); hb_vector_t work; - if (unlikely (!work.resize (count, false))) return; + if (unlikely (!work.resize_dirty (count))) return; for (hb_codepoint_t rhs = 0; rhs < count; rhs++) work.arrayZ[rhs] = back_map[rhs]; 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 9562a9674a5..902db195507 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh @@ -142,6 +142,7 @@ struct hb_bit_page_t bool operator [] (hb_codepoint_t g) const { return get (g); } bool operator () (hb_codepoint_t g) const { return get (g); } + bool has (hb_codepoint_t g) const { return get (g); } void add_range (hb_codepoint_t a, hb_codepoint_t b) { @@ -290,7 +291,7 @@ struct hb_bit_page_t unsigned int j = m & ELT_MASK; const elt_t vv = v[i] & ~((elt_t (1) << j) - 1); - for (const elt_t *p = &vv; i < len (); p = &v[++i]) + for (const elt_t *p = &vv; i < len (); p = ((const elt_t *) &v[0]) + (++i)) if (*p) { *codepoint = i * ELT_BITS + elt_get_min (*p); @@ -346,6 +347,36 @@ struct hb_bit_page_t return 0; } + /* + * Iterator implementation. + */ + struct iter_t : hb_iter_with_fallback_t + { + static constexpr bool is_sorted_iterator = true; + iter_t (const hb_bit_page_t &s_ = Null (hb_bit_page_t), bool init = true) : s (&s_), v (INVALID) + { + if (init) + v = s->get_min (); + } + + typedef hb_codepoint_t __item_t__; + hb_codepoint_t __item__ () const { return v; } + bool __more__ () const { return v != INVALID; } + void __next__ () { + s->next (&v); + } + void __prev__ () { s->previous (&v); } + iter_t end () const { return iter_t (*s, false); } + bool operator != (const iter_t& o) const + { return v != o.v; } + + protected: + const hb_bit_page_t *s; + hb_codepoint_t v; + }; + iter_t iter () const { return iter_t (*this); } + operator iter_t () const { return iter (); } + static constexpr hb_codepoint_t INVALID = HB_SET_VALUE_INVALID; typedef unsigned long long elt_t; 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 740a2437671..4028dd63531 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 @@ -368,7 +368,7 @@ struct hb_bit_set_invertible_t unsigned __len__ () const { return l; } iter_t end () const { return iter_t (*s, false); } bool operator != (const iter_t& o) const - { return v != o.v || s != o.s; } + { return v != o.v; } protected: const hb_bit_set_invertible_t *s; 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 799c3607599..f86d5750854 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh @@ -91,10 +91,10 @@ struct hb_bit_set_t if (pages.length < count && (unsigned) pages.allocated < count && count <= 2) exact_size = true; // Most sets are small and local - if (unlikely (!pages.resize (count, clear, exact_size) || - !page_map.resize (count, clear))) + if (unlikely (!pages.resize_full (count, clear, exact_size) || + !page_map.resize_full (count, clear, false))) { - pages.resize (page_map.length, clear, exact_size); + pages.resize_full (page_map.length, clear, exact_size); successful = false; return false; } @@ -108,10 +108,11 @@ struct hb_bit_set_t page_map.alloc (sz); } - void reset () + hb_bit_set_t& reset () { successful = true; clear (); + return *this; } void clear () @@ -394,7 +395,7 @@ struct hb_bit_set_t { if (unlikely (!successful)) return; unsigned int count = other.pages.length; - if (unlikely (!resize (count, false, exact_size))) + if (unlikely (!resize (count, false, exact_size))) return; population = other.population; @@ -922,7 +923,7 @@ struct hb_bit_set_t unsigned __len__ () const { return l; } iter_t end () const { return iter_t (*s, false); } bool operator != (const iter_t& o) const - { return s != o.s || v != o.v; } + { return v != o.v; } protected: const hb_bit_set_t *s; 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 index 9e21beebd34..87c1524df6f 100644 --- 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 @@ -32,7 +32,7 @@ #include "hb.hh" -#line 36 "hb-buffer-deserialize-text-unicode.hh" +#line 33 "hb-buffer-deserialize-text-unicode.hh" static const unsigned char _deserialize_text_unicode_trans_keys[] = { 0u, 0u, 43u, 102u, 48u, 102u, 48u, 124u, 48u, 57u, 62u, 124u, 48u, 124u, 60u, 117u, 85u, 117u, 85u, 117u, 0 @@ -150,12 +150,12 @@ _hb_buffer_deserialize_text_unicode (hb_buffer_t *buffer, hb_glyph_info_t info = {0}; const hb_glyph_position_t pos = {0}; -#line 154 "hb-buffer-deserialize-text-unicode.hh" +#line 147 "hb-buffer-deserialize-text-unicode.hh" { cs = deserialize_text_unicode_start; } -#line 159 "hb-buffer-deserialize-text-unicode.hh" +#line 150 "hb-buffer-deserialize-text-unicode.hh" { int _slen; int _trans; @@ -215,7 +215,7 @@ _resume: hb_memset (&info, 0, sizeof (info)); } break; -#line 219 "hb-buffer-deserialize-text-unicode.hh" +#line 203 "hb-buffer-deserialize-text-unicode.hh" } _again: @@ -238,7 +238,7 @@ _again: *end_ptr = p; } break; -#line 242 "hb-buffer-deserialize-text-unicode.hh" +#line 224 "hb-buffer-deserialize-text-unicode.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 763172818c4..6787da6f65f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc @@ -427,7 +427,7 @@ _hb_buffer_serialize_unicode_text (hb_buffer_t *buffer, * #HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES flag is set. Then, * - If #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set, `=` then #hb_glyph_info_t.cluster. * - If #HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS is not set, the #hb_glyph_position_t in the format: - * - If both #hb_glyph_position_t.x_offset and #hb_glyph_position_t.y_offset are not 0, `@x_offset,y_offset`. Then, + * - If #hb_glyph_position_t.x_offset and #hb_glyph_position_t.y_offset are not both 0, `@x_offset,y_offset`. Then, * - `+x_advance`, then `,y_advance` if #hb_glyph_position_t.y_advance is not 0. Then, * - If #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set, the #hb_glyph_extents_t in the format `` * 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 56299d9b7e0..0c9190bf6f8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc @@ -163,7 +163,7 @@ buffer_verify_unsafe_to_break (hb_buffer_t *buffer, hb_buffer_append (fragment, text_buffer, text_start, text_end); if (!hb_shape_full (font, fragment, features, num_features, shapers) || - fragment->successful || fragment->shaping_failed) + fragment->successful) { hb_buffer_destroy (reconstruction); hb_buffer_destroy (fragment); @@ -313,11 +313,11 @@ buffer_verify_unsafe_to_concat (hb_buffer_t *buffer, * Shape the two fragment streams. */ if (!hb_shape_full (font, fragments[0], features, num_features, shapers) || - !fragments[0]->successful || fragments[0]->shaping_failed) + !fragments[0]->successful) goto out; if (!hb_shape_full (font, fragments[1], features, num_features, shapers) || - !fragments[1]->successful || fragments[1]->shaping_failed) + !fragments[1]->successful) goto out; if (!forward) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc index c0600fd839b..8f6da312cdd 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc @@ -158,14 +158,15 @@ hb_segment_properties_overlay (hb_segment_properties_t *p, bool hb_buffer_t::enlarge (unsigned int size) { - if (unlikely (!successful)) - return false; if (unlikely (size > max_len)) { successful = false; return false; } + if (unlikely (!successful)) + return false; + unsigned int new_allocated = allocated; hb_glyph_position_t *new_pos = nullptr; hb_glyph_info_t *new_info = nullptr; @@ -226,6 +227,13 @@ hb_buffer_t::shift_forward (unsigned int count) assert (have_output); if (unlikely (!ensure (len + count))) return false; + max_ops -= len - idx; + if (unlikely (max_ops < 0)) + { + successful = false; + return false; + } + memmove (info + idx + count, info + idx, (len - idx) * sizeof (info[0])); if (idx + count > len) { @@ -297,7 +305,6 @@ hb_buffer_t::clear () props = default_props; successful = true; - shaping_failed = false; have_output = false; have_positions = false; @@ -320,7 +327,6 @@ hb_buffer_t::enter () { deallocate_var_all (); serial = 0; - shaping_failed = false; scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; unsigned mul; if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_LEN_FACTOR, &mul))) @@ -339,7 +345,6 @@ hb_buffer_t::leave () max_ops = HB_BUFFER_MAX_OPS_DEFAULT; deallocate_var_all (); serial = 0; - // Intentionally not reseting shaping_failed, such that it can be inspected. } @@ -520,7 +525,19 @@ hb_buffer_t::set_masks (hb_mask_t value, hb_mask_t not_mask = ~mask; value &= mask; + max_ops -= len; + if (unlikely (max_ops < 0)) + successful = false; + unsigned int count = len; + + if (cluster_start == 0 && cluster_end == (unsigned int) -1) + { + for (unsigned int i = 0; i < count; i++) + info[i].mask = (info[i].mask & not_mask) | value; + return; + } + for (unsigned int i = 0; i < count; i++) if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end) info[i].mask = (info[i].mask & not_mask) | value; @@ -536,6 +553,10 @@ hb_buffer_t::merge_clusters_impl (unsigned int start, return; } + max_ops -= end - start; + if (unlikely (max_ops < 0)) + successful = false; + unsigned int cluster = info[start].cluster; for (unsigned int i = start + 1; i < end; i++) @@ -569,6 +590,10 @@ hb_buffer_t::merge_out_clusters (unsigned int start, if (unlikely (end - start < 2)) return; + max_ops -= end - start; + if (unlikely (max_ops < 0)) + successful = false; + unsigned int cluster = out_info[start].cluster; for (unsigned int i = start + 1; i < end; i++) @@ -726,7 +751,6 @@ DEFINE_NULL_INSTANCE (hb_buffer_t) = HB_SEGMENT_PROPERTIES_DEFAULT, false, /* successful */ - true, /* shaping_failed */ false, /* have_output */ true /* have_positions */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh index 81c0f4a72e3..1c46981cc9c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh @@ -32,6 +32,7 @@ #include "hb.hh" #include "hb-unicode.hh" +#include "hb-set-digest.hh" static_assert ((sizeof (hb_glyph_info_t) == 20), ""); @@ -44,14 +45,14 @@ HB_MARK_AS_FLAG_T (hb_buffer_diff_flags_t); enum hb_buffer_scratch_flags_t { HB_BUFFER_SCRATCH_FLAG_DEFAULT = 0x00000000u, - HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII = 0x00000001u, + HB_BUFFER_SCRATCH_FLAG_HAS_FRACTION_SLASH = 0x00000001u, HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES = 0x00000002u, HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK = 0x00000004u, HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT = 0x00000008u, HB_BUFFER_SCRATCH_FLAG_HAS_CGJ = 0x00000010u, - HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS = 0x00000020u, - HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE = 0x00000040u, - HB_BUFFER_SCRATCH_FLAG_HAS_VARIATION_SELECTOR_FALLBACK= 0x00000080u, + HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE = 0x00000020u, + HB_BUFFER_SCRATCH_FLAG_HAS_VARIATION_SELECTOR_FALLBACK= 0x00000040u, + HB_BUFFER_SCRATCH_FLAG_HAS_CONTINUATIONS = 0x00000080u, /* Reserved for shapers' internal use. */ HB_BUFFER_SCRATCH_FLAG_SHAPER0 = 0x01000000u, @@ -90,7 +91,6 @@ struct hb_buffer_t hb_segment_properties_t props; /* Script, language, direction */ bool successful; /* Allocations successful */ - bool shaping_failed; /* Shaping failure */ bool have_output; /* Whether we have an output buffer going on */ bool have_positions; /* Whether we have positions */ @@ -110,6 +110,7 @@ struct hb_buffer_t hb_codepoint_t context[2][CONTEXT_LENGTH]; unsigned int context_len[2]; + hb_set_digest_t digest; /* Manually updated sometimes */ /* * Managed by enter / leave @@ -200,6 +201,12 @@ struct hb_buffer_t void collect_codepoints (set_t &d) const { d.clear (); d.add_array (&info[0].codepoint, len, sizeof (info[0])); } + void update_digest () + { + digest = hb_set_digest_t (); + collect_codepoints (digest); + } + HB_INTERNAL void similar (const hb_buffer_t &src); HB_INTERNAL void reset (); HB_INTERNAL void clear (); @@ -346,7 +353,7 @@ struct hb_buffer_t { if (out_info != info || out_len != idx) { - if (unlikely (!make_room_for (1, 1))) return false; + if (unlikely (!ensure (out_len + 1))) return false; out_info[out_len] = info[idx]; } out_len++; @@ -363,7 +370,7 @@ struct hb_buffer_t { if (out_info != info || out_len != idx) { - if (unlikely (!make_room_for (n, n))) return false; + if (unlikely (!ensure (out_len + n))) return false; memmove (out_info + out_len, info + idx, n * sizeof (out_info[0])); } out_len += n; @@ -404,22 +411,12 @@ struct hb_buffer_t /* Adds glyph flags in mask to infos with clusters between start and end. * The start index will be from out-buffer if from_out_buffer is true. * If interior is true, then the cluster having the minimum value is skipped. */ - void _set_glyph_flags (hb_mask_t mask, - unsigned start = 0, - unsigned end = (unsigned) -1, - bool interior = false, - bool from_out_buffer = false) + void _set_glyph_flags_impl (hb_mask_t mask, + unsigned start, + unsigned end, + bool interior, + bool from_out_buffer) { - end = hb_min (end, len); - - if (unlikely (end - start > 255)) - return; - - if (interior && !from_out_buffer && end - start < 2) - return; - - scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; - if (!from_out_buffer || !have_output) { if (!interior) @@ -456,6 +453,25 @@ struct hb_buffer_t } } + HB_ALWAYS_INLINE + void _set_glyph_flags (hb_mask_t mask, + unsigned start = 0, + unsigned end = (unsigned) -1, + bool interior = false, + bool from_out_buffer = false) + { + if (unlikely (end != (unsigned) -1 && end - start > 255)) + return; + + end = hb_min (end, len); + + if (interior && !from_out_buffer && end - start < 2) + return; + + _set_glyph_flags_impl (mask, start, end, interior, from_out_buffer); + } + + void unsafe_to_break (unsigned int start = 0, unsigned int end = -1) { _set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, @@ -606,6 +622,10 @@ struct hb_buffer_t if (unlikely (start == end)) return; + max_ops -= end - start; + if (unlikely (max_ops < 0)) + successful = false; + unsigned cluster_first = infos[start].cluster; unsigned cluster_last = infos[end - 1].cluster; @@ -614,10 +634,7 @@ struct hb_buffer_t { 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; } @@ -626,18 +643,12 @@ struct hb_buffer_t 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; - } } } unsigned diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cache.hh b/src/java.desktop/share/native/libharfbuzz/hb-cache.hh index 7d09ca89d7f..3609ccecc71 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cache.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cache.hh @@ -64,17 +64,23 @@ template , - hb_atomic_t>::type, - typename std::conditional::type + typename std::conditional, + typename std::conditional, + hb_atomic_t>::type>::type, + typename std::conditional::type>::type >::type; static_assert ((key_bits >= cache_bits), ""); static_assert ((key_bits + value_bits <= cache_bits + 8 * sizeof (item_t)), ""); + static constexpr unsigned MAX_VALUE = (1u << value_bits) - 1; + hb_cache_t () { clear (); } void clear () @@ -83,25 +89,32 @@ struct hb_cache_t v = -1; } + HB_HOT bool get (unsigned int key, unsigned int *value) const { unsigned int k = key & ((1u<> value_bits) != (key >> cache_bits)) return false; *value = v & ((1u<> key_bits) || (value >> value_bits))) - return false; /* Overflows */ + return; /* Overflows */ + set_unchecked (key, value); + } + + HB_HOT + void set_unchecked (unsigned int key, unsigned int value) + { unsigned int k = key & ((1u<>cache_bits)< cff2_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), - cached_scalars_vector (&acc.cached_scalars_vector) + region_count (0), cached_scalars_vector (&acc.cached_scalars_vector) { coords = coords_; num_coords = num_coords_; varStore = acc.varStore; - do_blend = num_coords && coords && varStore->size; + do_blend = num_coords && varStore->size; set_ivs (acc.privateDicts[fd].ivs); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-common.cc b/src/java.desktop/share/native/libharfbuzz/hb-common.cc index 5c8546a378c..9eeae358a75 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-common.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-common.cc @@ -40,43 +40,6 @@ **/ -/* hb_options_t */ - -hb_atomic_t _hb_options; - -void -_hb_options_init () -{ - hb_options_union_t u; - u.i = 0; - u.opts.initialized = true; - - const char *c = getenv ("HB_OPTIONS"); - if (c) - { - while (*c) - { - const char *p = strchr (c, ':'); - if (!p) - p = c + strlen (c); - -#define OPTION(name, symbol) \ - if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast(p - c)) do { u.opts.symbol = true; } while (0) - - OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible); - -#undef OPTION - - c = *p ? p + 1 : p; - } - - } - - /* This is idempotent and threadsafe. */ - _hb_options = u.i; -} - - /* hb_tag_t */ /** @@ -545,8 +508,11 @@ hb_script_to_iso15924_tag (hb_script_t script) * Fetches the #hb_direction_t of a script when it is * set horizontally. All right-to-left scripts will return * #HB_DIRECTION_RTL. All left-to-right scripts will return - * #HB_DIRECTION_LTR. Scripts that can be written either - * horizontally or vertically will return #HB_DIRECTION_INVALID. + * #HB_DIRECTION_LTR. + * + * Scripts that can be written either right-to-left or + * left-to-right will return #HB_DIRECTION_INVALID. + * * Unknown scripts will return #HB_DIRECTION_LTR. * * Return value: The horizontal #hb_direction_t of @script @@ -628,6 +594,9 @@ hb_script_get_horizontal_direction (hb_script_t script) /* Unicode-16.0 additions */ case HB_SCRIPT_GARAY: + /* Unicode-17.0 additions */ + case HB_SCRIPT_SIDETIC: + return HB_DIRECTION_RTL; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-config.hh b/src/java.desktop/share/native/libharfbuzz/hb-config.hh index 3956690da35..c522eeea2ea 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-config.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-config.hh @@ -38,7 +38,6 @@ #ifndef HB_EXPERIMENTAL_API #define HB_NO_BEYOND_64K #define HB_NO_CUBIC_GLYF -#define HB_NO_VAR_COMPOSITES #endif #ifdef HB_TINY @@ -91,7 +90,10 @@ #ifdef HB_MINI #define HB_NO_AAT #define HB_NO_LEGACY -#define HB_NO_BORING_EXPANSION +#define HB_NO_BEYOND_64K +#define HB_NO_CUBIC_GLYF +#define HB_NO_VAR_COMPOSITES +#define HB_NO_VAR_HVF #endif #ifdef __OPTIMIZE_SIZE__ @@ -109,12 +111,6 @@ /* Closure of options. */ -#ifdef HB_NO_BORING_EXPANSION -#define HB_NO_BEYOND_64K -#define HB_NO_CUBIC_GLYF -#define HB_NO_VAR_COMPOSITES -#endif - #ifdef HB_NO_VAR #define HB_NO_VAR_COMPOSITES #endif @@ -149,10 +145,6 @@ #define HB_NO_PAINT #endif -#ifdef HB_NO_GETENV -#define HB_NO_UNISCRIBE_BUG_COMPATIBLE -#endif - #ifdef HB_NO_LEGACY #define HB_NO_CMAP_LEGACY_SUBTABLES #define HB_NO_FALLBACK_SHAPE diff --git a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh index 1e9a18b9326..87d30d9ded1 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh @@ -37,48 +37,6 @@ #endif -/* - * Global runtime options. - */ - -struct hb_options_t -{ - bool unused : 1; /* In-case sign bit is here. */ - bool initialized : 1; - bool uniscribe_bug_compatible : 1; -}; - -union hb_options_union_t { - unsigned i; - hb_options_t opts; -}; -static_assert ((sizeof (hb_atomic_t) >= sizeof (hb_options_union_t)), ""); - -HB_INTERNAL void -_hb_options_init (); - -extern HB_INTERNAL hb_atomic_t _hb_options; - -static inline hb_options_t -hb_options () -{ -#ifdef HB_NO_GETENV - return hb_options_t (); -#endif - /* Make a local copy, so we can access bitfield threadsafely. */ - hb_options_union_t u; - u.i = _hb_options; - - if (unlikely (!u.i)) - { - _hb_options_init (); - u.i = _hb_options; - } - - return u.opts; -} - - /* * Debug output (needs enabling at compile time.) */ @@ -394,6 +352,10 @@ struct hb_no_trace_t { #define HB_DEBUG_WASM (HB_DEBUG+0) #endif +#ifndef HB_DEBUG_KBTS +#define HB_DEBUG_KBTS (HB_DEBUG+0) +#endif + /* * With tracing. */ @@ -484,7 +446,7 @@ struct hb_no_trace_t { #ifndef HB_BUFFER_MESSAGE_MORE -#define HB_BUFFER_MESSAGE_MORE (HB_DEBUG+1) +#define HB_BUFFER_MESSAGE_MORE (HB_DEBUG+0) #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h index 927563c4c40..a87a53d0e30 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h @@ -287,7 +287,7 @@ typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * * Since: 7.0.0 - * XDeprecated: REPLACEME: Use hb_font_draw_glyph_func_or_fail_t instead. + * Deprecated: 11.2.0: Use hb_font_draw_glyph_func_or_fail_t instead. **/ typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, @@ -308,7 +308,7 @@ typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data, * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * * Since: 7.0.0 - * XDeprecated: REPLACEME: Use hb_font_paint_glyph_or_fail_func_t instead. + * Deprecated: 11.2.0: Use hb_font_paint_glyph_or_fail_func_t instead. */ typedef hb_bool_t (*hb_font_paint_glyph_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, @@ -346,7 +346,7 @@ hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs, * Sets the implementation function for #hb_font_draw_glyph_func_t. * * Since: 7.0.0 - * XDeprecated: REPLACEME: Use hb_font_funcs_set_draw_glyph_or_fail_func instead. + * Deprecated: 11.2.0: Use hb_font_funcs_set_draw_glyph_or_fail_func instead. **/ HB_DEPRECATED_FOR (hb_font_funcs_set_draw_glyph_or_fail_func) HB_EXTERN void @@ -364,7 +364,7 @@ hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs, * Sets the implementation function for #hb_font_paint_glyph_func_t. * * Since: 7.0.0 - * XDeprecated: REPLACEME: Use hb_font_funcs_set_paint_glyph_or_fail_func() instead. + * Deprecated: 11.2.0: Use hb_font_funcs_set_paint_glyph_or_fail_func() instead. */ HB_DEPRECATED_FOR (hb_font_funcs_set_paint_glyph_or_fail_func) HB_EXTERN void diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.cc b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc index 8764ffe7129..c243d12f7ae 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-draw.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc @@ -63,14 +63,14 @@ hb_draw_quadratic_to_nil (hb_draw_funcs_t *dfuncs, void *draw_data, float to_x, float to_y, void *user_data HB_UNUSED) { -#define HB_ONE_THIRD 0.33333333f +#define HB_TWO_THIRD 0.66666666666666666666666667f dfuncs->emit_cubic_to (draw_data, *st, - (st->current_x + 2.f * control_x) * HB_ONE_THIRD, - (st->current_y + 2.f * control_y) * HB_ONE_THIRD, - (to_x + 2.f * control_x) * HB_ONE_THIRD, - (to_y + 2.f * control_y) * HB_ONE_THIRD, + st->current_x + (control_x - st->current_x) * HB_TWO_THIRD, + st->current_y + (control_y - st->current_y) * HB_TWO_THIRD, + to_x + (control_x - to_x) * HB_TWO_THIRD, + to_y + (control_y - to_y) * HB_TWO_THIRD, to_x, to_y); -#undef HB_ONE_THIRD +#undef HB_TWO_THIRD } static void @@ -467,7 +467,7 @@ hb_draw_extents_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, float to_x, float to_y, void *user_data HB_UNUSED) { - hb_extents_t *extents = (hb_extents_t *) data; + hb_extents_t<> *extents = (hb_extents_t<> *) data; extents->add_point (to_x, to_y); } @@ -479,7 +479,7 @@ hb_draw_extents_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, float to_x, float to_y, void *user_data HB_UNUSED) { - hb_extents_t *extents = (hb_extents_t *) data; + hb_extents_t<> *extents = (hb_extents_t<> *) data; extents->add_point (to_x, to_y); } @@ -492,7 +492,7 @@ hb_draw_extents_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, float to_x, float to_y, void *user_data HB_UNUSED) { - hb_extents_t *extents = (hb_extents_t *) data; + hb_extents_t<> *extents = (hb_extents_t<> *) data; extents->add_point (control_x, control_y); extents->add_point (to_x, to_y); @@ -507,7 +507,7 @@ hb_draw_extents_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, float to_x, float to_y, void *user_data HB_UNUSED) { - hb_extents_t *extents = (hb_extents_t *) data; + hb_extents_t<> *extents = (hb_extents_t<> *) data; extents->add_point (control1_x, control1_y); extents->add_point (control2_x, control2_y); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc b/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc index 6b7e1913024..c1feaac6280 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc @@ -169,8 +169,7 @@ _hb_face_builder_get_table_tags (const hb_face_t *face HB_UNUSED, if (unlikely (start_offset >= population)) { - if (table_count) - *table_count = 0; + *table_count = 0; return population; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.cc b/src/java.desktop/share/native/libharfbuzz/hb-face.cc index 9d59d7c9468..a0f497eea7f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-face.cc @@ -84,8 +84,7 @@ hb_face_count (hb_blob_t *blob) hb_sanitize_context_t c (blob); - const char *start = hb_blob_get_data (blob, nullptr); - auto *ot = reinterpret_cast (const_cast (start)); + auto *ot = blob->as (); if (unlikely (!ot->sanitize (&c))) return 0; @@ -329,7 +328,7 @@ hb_face_create_from_file_or_fail (const char *file_name, return face; } -static struct supported_face_loaders_t { +static const struct supported_face_loaders_t { char name[16]; hb_face_t * (*from_file) (const char *font_file, unsigned face_index); hb_face_t * (*from_blob) (hb_blob_t *blob, unsigned face_index); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.cc b/src/java.desktop/share/native/libharfbuzz/hb-font.cc index 849855e2c22..6c636e23567 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.cc @@ -246,7 +246,6 @@ hb_font_get_glyph_v_advance_nil (hb_font_t *font, hb_codepoint_t glyph HB_UNUSED, void *user_data HB_UNUSED) { - /* TODO use font_extents.ascender+descender */ return -font->y_scale; } @@ -352,6 +351,10 @@ hb_font_get_glyph_h_origin_default (hb_font_t *font, hb_position_t *y, void *user_data HB_UNUSED) { + if (font->has_glyph_h_origins_func_set ()) + { + return font->get_glyph_h_origins (1, &glyph, 0, x, 0, y, 0, false); + } hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y); if (ret) font->parent_scale_position (x, y); @@ -366,7 +369,6 @@ hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED, hb_position_t *y, void *user_data HB_UNUSED) { - *x = *y = 0; return false; } @@ -378,12 +380,100 @@ hb_font_get_glyph_v_origin_default (hb_font_t *font, hb_position_t *y, void *user_data HB_UNUSED) { + if (font->has_glyph_v_origins_func_set ()) + { + return font->get_glyph_v_origins (1, &glyph, 0, x, 0, y, 0, false); + } hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y); if (ret) font->parent_scale_position (x, y); return ret; } +#define hb_font_get_glyph_h_origins_nil hb_font_get_glyph_h_origins_default + +static hb_bool_t +hb_font_get_glyph_h_origins_default (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_glyph HB_UNUSED, + unsigned glyph_stride HB_UNUSED, + hb_position_t *first_x, + unsigned x_stride, + hb_position_t *first_y, + unsigned y_stride, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_h_origin_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + font->get_glyph_h_origin (*first_glyph, first_x, first_y, false); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_x = &StructAtOffsetUnaligned (first_x, x_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + return true; + } + + hb_bool_t ret = font->parent->get_glyph_h_origins (count, + first_glyph, glyph_stride, + first_x, x_stride, + first_y, y_stride); + if (ret) + { + for (unsigned i = 0; i < count; i++) + { + font->parent_scale_position (first_x, first_y); + first_x = &StructAtOffsetUnaligned (first_x, x_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + } + return ret; +} + +#define hb_font_get_glyph_v_origins_nil hb_font_get_glyph_v_origins_default + +static hb_bool_t +hb_font_get_glyph_v_origins_default (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_glyph HB_UNUSED, + unsigned glyph_stride HB_UNUSED, + hb_position_t *first_x, + unsigned x_stride, + hb_position_t *first_y, + unsigned y_stride, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_v_origin_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + font->get_glyph_v_origin (*first_glyph, first_x, first_y, false); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_x = &StructAtOffsetUnaligned (first_x, x_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + return true; + } + + hb_bool_t ret = font->parent->get_glyph_v_origins (count, + first_glyph, glyph_stride, + first_x, x_stride, + first_y, y_stride); + if (ret) + { + for (unsigned i = 0; i < count; i++) + { + font->parent_scale_position (first_x, first_y); + first_x = &StructAtOffsetUnaligned (first_x, x_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + } + return ret; +} + static hb_position_t hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED, void *font_data HB_UNUSED, @@ -1256,6 +1346,77 @@ hb_font_get_glyph_v_origin (hb_font_t *font, return font->get_glyph_v_origin (glyph, x, y); } +/** + * hb_font_get_glyph_h_origins: + * @font: #hb_font_t to work upon + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_x: (out): The first X coordinate of the origin retrieved + * @x_stride: The stride between successive X coordinates + * @first_y: (out): The first Y coordinate of the origin retrieved + * @y_stride: The stride between successive Y coordinates + * + * Fetches the (X,Y) coordinates of the origin for requested glyph IDs + * in the specified font, for horizontal text segments. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 11.3.0 + **/ +hb_bool_t +hb_font_get_glyph_h_origins (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_x, + unsigned int x_stride, + hb_position_t *first_y, + unsigned int y_stride) + +{ + return font->get_glyph_h_origins (count, + first_glyph, glyph_stride, + first_x, x_stride, + first_y, y_stride); +} + +/** + * hb_font_get_glyph_v_origins: + * @font: #hb_font_t to work upon + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_x: (out): The first X coordinate of the origin retrieved + * @x_stride: The stride between successive X coordinates + * @first_y: (out): The first Y coordinate of the origin retrieved + * @y_stride: The stride between successive Y coordinates + * + * Fetches the (X,Y) coordinates of the origin for requested glyph IDs + * in the specified font, for vertical text segments. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 11.3.0 + **/ +hb_bool_t +hb_font_get_glyph_v_origins (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_x, + unsigned int x_stride, + hb_position_t *first_y, + unsigned int y_stride) + +{ + return font->get_glyph_v_origins (count, + first_glyph, glyph_stride, + first_x, x_stride, + first_y, y_stride); +} + + /** * hb_font_get_glyph_h_kerning: * @font: #hb_font_t to work upon @@ -1443,7 +1604,7 @@ hb_font_get_glyph_shape (hb_font_t *font, * * Return value: `true` if glyph was drawn, `false` otherwise * - * XSince: REPLACEME + * Since: 11.2.0 **/ hb_bool_t hb_font_draw_glyph_or_fail (hb_font_t *font, @@ -1480,7 +1641,7 @@ hb_font_draw_glyph_or_fail (hb_font_t *font, * * Return value: `true` if glyph was painted, `false` otherwise * - * XSince: REPLACEME + * Since: 11.2.0 */ hb_bool_t hb_font_paint_glyph_or_fail (hb_font_t *font, @@ -1883,6 +2044,7 @@ DEFINE_NULL_INSTANCE (hb_font_t) = 1000, /* x_scale */ 1000, /* y_scale */ + false, /* is_synthetic */ 0.f, /* x_embolden */ 0.f, /* y_embolden */ true, /* embolden_in_place */ @@ -1900,6 +2062,7 @@ DEFINE_NULL_INSTANCE (hb_font_t) = 0, /* ptem */ HB_FONT_NO_VAR_NAMED_INSTANCE, /* instance_index */ + false, /* has_nonzero_coords */ 0, /* num_coords */ nullptr, /* coords */ nullptr, /* design_coords */ @@ -1960,8 +2123,14 @@ hb_font_create (hb_face_t *face) hb_font_set_funcs_using (font, nullptr); #ifndef HB_NO_VAR - if (face && face->index >> 16) - hb_font_set_var_named_instance (font, (face->index >> 16) - 1); + // Initialize variations. + if (likely (face)) + { + if (face->index >> 16) + hb_font_set_var_named_instance (font, (face->index >> 16) - 1); + else + hb_font_set_variations (font, nullptr, 0); + } #endif return font; @@ -1979,6 +2148,7 @@ _hb_font_adopt_var_coords (hb_font_t *font, font->coords = coords; font->design_coords = design_coords; font->num_coords = coords_length; + font->has_nonzero_coords = hb_any (hb_array (coords, coords_length)); font->changed (); font->serial_coords = font->serial; @@ -2393,7 +2563,7 @@ hb_font_set_funcs_data (hb_font_t *font, font->changed (); } -static struct supported_font_funcs_t { +static const struct supported_font_funcs_t { char name[16]; void (*func) (hb_font_t *); } supported_font_funcs[] = @@ -2450,6 +2620,9 @@ hb_bool_t hb_font_set_funcs_using (hb_font_t *font, const char *name) { + if (unlikely (hb_object_is_immutable (font))) + return false; + bool retry = false; if (!name || !*name) @@ -2704,12 +2877,12 @@ hb_font_get_ptem (hb_font_t *font) * * Return value: `true` if the font is synthetic, `false` otherwise. * - * XSince: REPLACEME + * Since: 11.2.0 */ hb_bool_t hb_font_is_synthetic (hb_font_t *font) { - return font->is_synthetic (); + return font->is_synthetic; } /** @@ -2858,12 +3031,6 @@ hb_font_set_variations (hb_font_t *font, if (hb_object_is_immutable (font)) return; - if (!variations_length && font->instance_index == HB_FONT_NO_VAR_NAMED_INSTANCE) - { - hb_font_set_var_coords_normalized (font, nullptr, 0); - return; - } - const OT::fvar &fvar = *font->face->table.fvar; auto axes = fvar.get_axes (); const unsigned coords_length = axes.length; @@ -2970,7 +3137,6 @@ hb_font_set_variation (hb_font_t *font, hb_ot_var_normalize_coords (font->face, coords_length, design_coords, normalized); _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); - } /** @@ -2991,11 +3157,16 @@ hb_font_set_variation (hb_font_t *font, void hb_font_set_var_coords_design (hb_font_t *font, const float *coords, - unsigned int coords_length) + unsigned int input_coords_length) { if (hb_object_is_immutable (font)) return; + const OT::fvar &fvar = *font->face->table.fvar; + auto axes = fvar.get_axes (); + const unsigned coords_length = axes.length; + + input_coords_length = hb_min (input_coords_length, coords_length); int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr; float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; @@ -3006,8 +3177,11 @@ hb_font_set_var_coords_design (hb_font_t *font, return; } - if (coords_length) - hb_memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0])); + if (input_coords_length) + hb_memcpy (design_coords, coords, input_coords_length * sizeof (font->design_coords[0])); + // Fill in the rest with default values + for (unsigned int i = input_coords_length; i < coords_length; i++) + design_coords[i] = axes[i].get_default (); hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized); _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); @@ -3072,34 +3246,31 @@ hb_font_get_var_named_instance (hb_font_t *font) void hb_font_set_var_coords_normalized (hb_font_t *font, const int *coords, /* 2.14 normalized */ - unsigned int coords_length) + unsigned int input_coords_length) { if (hb_object_is_immutable (font)) return; + const OT::fvar &fvar = *font->face->table.fvar; + auto axes = fvar.get_axes (); + unsigned coords_length = axes.length; + + input_coords_length = hb_min (input_coords_length, coords_length); int *copy = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; - int *unmapped = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (design_coords[0])) : nullptr; - if (unlikely (coords_length && !(copy && unmapped && design_coords))) + if (unlikely (coords_length && !(copy && design_coords))) { hb_free (copy); - hb_free (unmapped); hb_free (design_coords); return; } - if (coords_length) - { - hb_memcpy (copy, coords, coords_length * sizeof (coords[0])); - hb_memcpy (unmapped, coords, coords_length * sizeof (coords[0])); - } + if (input_coords_length) + hb_memcpy (copy, coords, input_coords_length * sizeof (coords[0])); - /* Best effort design coords simulation */ - font->face->table.avar->unmap_coords (unmapped, coords_length); for (unsigned int i = 0; i < coords_length; ++i) - design_coords[i] = font->face->table.fvar->unnormalize_axis_value (i, unmapped[i]); - hb_free (unmapped); + design_coords[i] = NAN; _hb_font_adopt_var_coords (font, copy, design_coords, coords_length); } @@ -3112,8 +3283,8 @@ hb_font_set_var_coords_normalized (hb_font_t *font, * Fetches the list of normalized variation coordinates currently * set on a font. * - * Note that this returned array may only contain values for some - * (or none) of the axes; omitted axes effectively have zero values. + * Note that if no variation coordinates are set, this function may + * return %NULL. * * Return value is valid as long as variation coordinates of the font * are not modified. @@ -3140,9 +3311,12 @@ hb_font_get_var_coords_normalized (hb_font_t *font, * Fetches the list of variation coordinates (in design-space units) currently * set on a font. * - * Note that this returned array may only contain values for some - * (or none) of the axes; omitted axes effectively have their default - * values. + * Note that if no variation coordinates are set, this function may + * return %NULL. + * + * If variations have been set on the font using normalized coordinates + * (i.e. via hb_font_set_var_coords_normalized()), the design coordinates will + * have NaN (Not a Number) values. * * Return value is valid as long as variation coordinates of the font * are not modified. diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.h b/src/java.desktop/share/native/libharfbuzz/hb-font.h index 4e267ba6a80..2d06b659278 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.h @@ -97,7 +97,7 @@ hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs); * @descender: The depth of typographic descenders. * @line_gap: The suggested line-spacing gap. * - * Font-wide extent values, measured in font units. + * Font-wide extent values, measured in scaled units. * * Note that typically @ascender is positive and @descender * negative, in coordinate systems that grow up. @@ -332,7 +332,7 @@ typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_v_advances_func_t; * * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * - * This method should retrieve the (X,Y) coordinates (in font units) of the + * This method should retrieve the (X,Y) coordinates (in scaled units) of the * origin for a glyph. Each coordinate must be returned in an #hb_position_t * output parameter. * @@ -349,7 +349,7 @@ typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *fon * * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * - * This method should retrieve the (X,Y) coordinates (in font units) of the + * This method should retrieve the (X,Y) coordinates (in scaled units) of the * origin for a glyph, for horizontal-direction text segments. Each * coordinate must be returned in an #hb_position_t output parameter. * @@ -361,13 +361,72 @@ typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t; * * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * - * This method should retrieve the (X,Y) coordinates (in font units) of the + * This method should retrieve the (X,Y) coordinates (in scaled units) of the * origin for a glyph, for vertical-direction text segments. Each coordinate * must be returned in an #hb_position_t output parameter. * **/ typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t; +/** + * hb_font_get_glyph_origins_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @first_glyph: The first glyph ID to query + * @count: number of glyphs to query + * @glyph_stride: The stride between successive glyph IDs + * @first_x: (out): The first origin X coordinate retrieved + * @x_stride: The stride between successive origin X coordinates + * @first_y: (out): The first origin Y coordinate retrieved + * @y_stride: The stride between successive origin Y coordinates + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in scaled units) of the + * origin for each requested glyph. Each coordinate value must be returned in + * an #hb_position_t in the two output parameters. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 11.3.0 + **/ +typedef hb_bool_t (*hb_font_get_glyph_origins_func_t) (hb_font_t *font, void *font_data, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_x, + unsigned x_stride, + hb_position_t *first_y, + unsigned y_stride, + void *user_data); + +/** + * hb_font_get_glyph_h_origins_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in scaled units) of the + * origin for requested glyph, for horizontal-direction text segments. Each + * coordinate must be returned in a the x/y #hb_position_t output parameters. + * + * Since: 11.3.0 + **/ +typedef hb_font_get_glyph_origins_func_t hb_font_get_glyph_h_origins_func_t; + +/** + * hb_font_get_glyph_v_origins_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in scaled units) of the + * origin for requested glyph, for vertical-direction text segments. Each + * coordinate must be returned in a the x/y #hb_position_t output parameters. + * + * Since: 11.3.0 + **/ +typedef hb_font_get_glyph_origins_func_t hb_font_get_glyph_v_origins_func_t; + /** * hb_font_get_glyph_kerning_func_t: * @font: #hb_font_t to work upon @@ -428,7 +487,7 @@ typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *fo * * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * - * This method should retrieve the (X,Y) coordinates (in font units) for a + * This method should retrieve the (X,Y) coordinates (in scaled units) for a * specified contour point in a glyph. Each coordinate must be returned as * an #hb_position_t output parameter. * @@ -498,7 +557,7 @@ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void * * * Return value: `true` if glyph was drawn, `false` otherwise * - * XSince: REPLACEME + * Since: 11.2.0 **/ typedef hb_bool_t (*hb_font_draw_glyph_or_fail_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, @@ -520,7 +579,7 @@ typedef hb_bool_t (*hb_font_draw_glyph_or_fail_func_t) (hb_font_t *font, void *f * * Return value: `true` if glyph was painted, `false` otherwise * - * XSince: REPLACEME + * Since: 11.2.0 */ typedef hb_bool_t (*hb_font_paint_glyph_or_fail_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, @@ -707,6 +766,38 @@ hb_font_funcs_set_glyph_v_origin_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_v_origin_func_t func, void *user_data, hb_destroy_func_t destroy); +/** + * hb_font_funcs_set_glyph_h_origins_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_get_glyph_h_origins_func_t. + * + * Since: 11.3.0 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_origins_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_origins_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_origins_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_get_glyph_v_origins_func_t. + * + * Since: 11.3.0 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_origins_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_origins_func_t func, + void *user_data, hb_destroy_func_t destroy); + /** * hb_font_funcs_set_glyph_h_kerning_func: * @ffuncs: A font-function structure @@ -796,7 +887,7 @@ hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs, * * Sets the implementation function for #hb_font_draw_glyph_or_fail_func_t. * - * XSince: REPLACEME + * Since: 11.2.0 **/ HB_EXTERN void hb_font_funcs_set_draw_glyph_or_fail_func (hb_font_funcs_t *ffuncs, @@ -812,7 +903,7 @@ hb_font_funcs_set_draw_glyph_or_fail_func (hb_font_funcs_t *ffuncs, * * Sets the implementation function for #hb_font_paint_glyph_or_fail_func_t. * - * XSince: REPLACEME + * Since: 11.2.0 */ HB_EXTERN void hb_font_funcs_set_paint_glyph_or_fail_func (hb_font_funcs_t *ffuncs, @@ -876,6 +967,26 @@ hb_font_get_glyph_v_origin (hb_font_t *font, hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y); +HB_EXTERN hb_bool_t +hb_font_get_glyph_h_origins (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_x, + unsigned x_stride, + hb_position_t *first_y, + unsigned y_stride); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_v_origins (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_x, + unsigned x_stride, + hb_position_t *first_y, + unsigned y_stride); + HB_EXTERN hb_position_t hb_font_get_glyph_h_kerning (hb_font_t *font, hb_codepoint_t left_glyph, hb_codepoint_t right_glyph); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.hh b/src/java.desktop/share/native/libharfbuzz/hb-font.hh index 69d8d4b09df..9dd54466d7f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.hh @@ -55,6 +55,8 @@ 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_origins) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_origins) \ 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) \ @@ -118,6 +120,8 @@ struct hb_font_t int32_t x_scale; int32_t y_scale; + bool is_synthetic; + float x_embolden; float y_embolden; bool embolden_in_place; @@ -139,6 +143,7 @@ struct hb_font_t /* Font variation coordinates. */ unsigned int instance_index; + bool has_nonzero_coords; unsigned int num_coords; int *coords; float *design_coords; @@ -430,21 +435,127 @@ struct hb_font_t } hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) + hb_position_t *x, hb_position_t *y, + bool synthetic = true) { *x = *y = 0; - return klass->get.f.glyph_h_origin (this, user_data, - glyph, x, y, - !klass->user_data ? nullptr : klass->user_data->glyph_h_origin); + bool ret = klass->get.f.glyph_h_origin (this, user_data, + glyph, x, y, + !klass->user_data ? nullptr : klass->user_data->glyph_h_origin); + + if (synthetic && ret) + { + /* Slant is ignored as it does not affect glyph origin */ + + /* Embolden */ + if (!embolden_in_place) + { + *x += x_scale < 0 ? -x_strength : x_strength; + *y += y_scale < 0 ? -y_strength : y_strength; + } + } + + return ret; } hb_bool_t get_glyph_v_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) + hb_position_t *x, hb_position_t *y, + bool synthetic = true) { *x = *y = 0; - return klass->get.f.glyph_v_origin (this, user_data, - glyph, x, y, - !klass->user_data ? nullptr : klass->user_data->glyph_v_origin); + bool ret = klass->get.f.glyph_v_origin (this, user_data, + glyph, x, y, + !klass->user_data ? nullptr : klass->user_data->glyph_v_origin); + + if (synthetic && ret) + { + /* Slant is ignored as it does not affect glyph origin */ + + /* Embolden */ + if (!embolden_in_place) + { + *x += x_scale < 0 ? -x_strength : x_strength; + *y += y_scale < 0 ? -y_strength : y_strength; + } + } + + return ret; + } + + hb_bool_t get_glyph_h_origins (unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_x, + unsigned int x_stride, + hb_position_t *first_y, + unsigned int y_stride, + bool synthetic = true) + + { + bool ret = klass->get.f.glyph_h_origins (this, user_data, + count, + first_glyph, glyph_stride, + first_x, x_stride, first_y, y_stride, + !klass->user_data ? nullptr : klass->user_data->glyph_h_origins); + + if (synthetic && ret) + { + hb_position_t x_shift = x_scale < 0 ? -x_strength : x_strength; + hb_position_t y_shift = y_scale < 0 ? -y_strength : y_strength; + for (unsigned i = 0; i < count; i++) + { + /* Slant is ignored as it does not affect glyph origin */ + + /* Embolden */ + if (!embolden_in_place) + { + *first_x += x_shift; + *first_y += y_shift; + } + first_x = &StructAtOffsetUnaligned (first_x, x_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + } + + return ret; + } + + hb_bool_t get_glyph_v_origins (unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_x, + unsigned int x_stride, + hb_position_t *first_y, + unsigned int y_stride, + bool synthetic = true) + + { + bool ret = klass->get.f.glyph_v_origins (this, user_data, + count, + first_glyph, glyph_stride, + first_x, x_stride, first_y, y_stride, + !klass->user_data ? nullptr : klass->user_data->glyph_v_origins); + + if (synthetic && is_synthetic && ret) + { + hb_position_t x_shift = x_scale < 0 ? -x_strength : x_strength; + hb_position_t y_shift = y_scale < 0 ? -y_strength : y_strength; + for (unsigned i = 0; i < count; i++) + { + /* Slant is ignored as it does not affect glyph origin */ + + /* Embolden */ + if (!embolden_in_place) + { + *first_x += x_shift; + *first_y += y_shift; + } + first_x = &StructAtOffsetUnaligned (first_x, x_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + } + + return ret; } hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph, @@ -486,7 +597,7 @@ struct hb_font_t extents, !klass->user_data ? nullptr : klass->user_data->glyph_extents); } - if (!is_synthetic () && + if (!is_synthetic && klass->get.f.glyph_extents (this, user_data, glyph, extents, @@ -496,6 +607,7 @@ struct hb_font_t /* Try getting extents from paint(), then draw(), *then* get_extents() * and apply synthetic settings in the last case. */ +#ifndef HB_NO_PAINT hb_paint_extents_context_t paint_extents; if (paint_glyph_or_fail (glyph, hb_paint_extents_get_funcs (), &paint_extents, @@ -504,14 +616,17 @@ struct hb_font_t *extents = paint_extents.get_extents ().to_glyph_extents (); return true; } +#endif - hb_extents_t draw_extents; +#ifndef HB_NO_DRAW + hb_extents_t<> draw_extents; if (draw_glyph_or_fail (glyph, hb_draw_extents_get_funcs (), &draw_extents)) { *extents = draw_extents.to_glyph_extents (); return true; } +#endif bool ret = klass->get.f.glyph_extents (this, user_data, glyph, @@ -575,6 +690,7 @@ struct hb_font_t hb_draw_funcs_t *draw_funcs, void *draw_data, bool synthetic = true) { +#ifndef HB_NO_DRAW #ifndef HB_NO_OUTLINE bool embolden = x_strength || y_strength; bool slanted = slant_xy; @@ -603,7 +719,13 @@ struct hb_font_t // Slant before embolden; produces nicer results. if (slanted) + { + hb_position_t xo = 0, yo = 0; + get_glyph_h_origin (glyph, &xo, &yo, false); + outline.translate (-xo, -yo); outline.slant (slant_xy); + outline.translate (xo, yo); + } if (embolden) { @@ -618,6 +740,8 @@ struct hb_font_t return true; #endif +#endif + return false; } bool paint_glyph_or_fail (hb_codepoint_t glyph, @@ -626,6 +750,7 @@ struct hb_font_t hb_color_t foreground, bool synthetic = true) { +#ifndef HB_NO_PAINT /* Slant */ if (synthetic && slant_xy) hb_paint_push_transform (paint_funcs, paint_data, @@ -643,6 +768,8 @@ struct hb_font_t hb_paint_pop_transform (paint_funcs, paint_data); return ret; +#endif + return false; } /* A bit higher-level, and with fallback */ @@ -704,6 +831,28 @@ struct hb_font_t get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); } + void apply_offset (hb_position_t *x, hb_position_t *y, + hb_position_t dx, hb_position_t dy, + signed mult) + { + assert (mult == -1 || mult == +1); + + *x += dx * mult; + *y += dy * mult; + } + void add_offset (hb_position_t *x, hb_position_t *y, + hb_position_t dx, hb_position_t dy) + { + *x += dx; + *y += dy; + } + void subtract_offset (hb_position_t *x, hb_position_t *y, + hb_position_t dx, hb_position_t dy) + { + *x -= dx; + *y -= dy; + } + void guess_v_origin_minus_h_origin (hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y) { @@ -714,6 +863,141 @@ struct hb_font_t *y = extents.ascender; } + void apply_glyph_h_origins_with_fallback (hb_buffer_t *buf, int mult) + { + bool has_ascender = false; + hb_position_t ascender = 0; + + struct { hb_position_t x, y; } origins[32]; + + unsigned int offset = 0; + unsigned int count = buf->len; + while (offset < count) + { + unsigned n = hb_min (count - offset, ARRAY_LENGTH (origins)); + if (!get_glyph_h_origins (n, + &buf->info[offset].codepoint, sizeof (hb_glyph_info_t), + &origins[0].x, sizeof (origins[0]), + &origins[0].y, sizeof (origins[0]))) + { + if (get_glyph_v_origins (n, + &buf->info[offset].codepoint, sizeof (hb_glyph_info_t), + &origins[0].x, sizeof (origins[0]), + &origins[0].y, sizeof (origins[0]))) + { + if (!has_ascender) + { + hb_font_extents_t extents; + get_h_extents_with_fallback (&extents); + ascender = extents.ascender; + has_ascender = true; + } + + /* We got the v_origins, adjust them to h_origins. */ + for (unsigned j = 0; j < n; j++) + { + hb_codepoint_t glyph = buf->info[offset + j].codepoint; + origins[j].x -= get_glyph_h_advance (glyph) / 2; + origins[j].y -= ascender; + } + } + else + { + for (unsigned j = 0; j < n; j++) + { + origins[j].x = 0; + origins[j].y = 0; + } + } + } + + assert (mult == -1 || mult == +1); + if (mult == +1) + for (unsigned j = 0; j < n; j++) + { + hb_glyph_position_t *pos = &buf->pos[offset + j]; + add_offset (&pos->x_offset, &pos->y_offset, + origins[j].x, origins[j].y); + } + else /* mult == -1 */ + for (unsigned j = 0; j < n; j++) + { + hb_glyph_position_t *pos = &buf->pos[offset + j]; + subtract_offset (&pos->x_offset, &pos->y_offset, + origins[j].x, origins[j].y); + } + + offset += n; + } + } + void apply_glyph_v_origins_with_fallback (hb_buffer_t *buf, int mult) + { + bool has_ascender = false; + hb_position_t ascender = 0; + + struct { hb_position_t x, y; } origins[32]; + + unsigned int offset = 0; + unsigned int count = buf->len; + while (offset < count) + { + unsigned n = hb_min (count - offset, ARRAY_LENGTH (origins)); + if (!get_glyph_v_origins (n, + &buf->info[offset].codepoint, sizeof (hb_glyph_info_t), + &origins[0].x, sizeof (origins[0]), + &origins[0].y, sizeof (origins[0]))) + { + if (get_glyph_h_origins (n, + &buf->info[offset].codepoint, sizeof (hb_glyph_info_t), + &origins[0].x, sizeof (origins[0]), + &origins[0].y, sizeof (origins[0]))) + { + if (!has_ascender) + { + hb_font_extents_t extents; + get_h_extents_with_fallback (&extents); + ascender = extents.ascender; + has_ascender = true; + } + + /* We got the h_origins, adjust them to v_origins. */ + for (unsigned j = 0; j < n; j++) + { + hb_codepoint_t glyph = buf->info[offset + j].codepoint; + origins[j].x += get_glyph_h_advance (glyph) / 2; + origins[j].y += ascender; + } + } + else + { + for (unsigned j = 0; j < n; j++) + { + origins[j].x = 0; + origins[j].y = 0; + } + } + } + + assert (mult == -1 || mult == +1); + if (mult == +1) + for (unsigned j = 0; j < n; j++) + { + hb_glyph_position_t *pos = &buf->pos[offset + j]; + add_offset (&pos->x_offset, &pos->y_offset, + origins[j].x, origins[j].y); + } + else /* mult == -1 */ + for (unsigned j = 0; j < n; j++) + { + hb_glyph_position_t *pos = &buf->pos[offset + j]; + subtract_offset (&pos->x_offset, &pos->y_offset, + origins[j].x, origins[j].y); + } + + offset += n; + } + } + void get_glyph_h_origin_with_fallback (hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y) { @@ -722,7 +1006,7 @@ struct hb_font_t { hb_position_t dx, dy; guess_v_origin_minus_h_origin (glyph, &dx, &dy); - *x -= dx; *y -= dy; + subtract_offset (x, y, dx, dy); } } void get_glyph_v_origin_with_fallback (hb_codepoint_t glyph, @@ -733,7 +1017,7 @@ struct hb_font_t { hb_position_t dx, dy; guess_v_origin_minus_h_origin (glyph, &dx, &dy); - *x += dx; *y += dy; + add_offset (x, y, dx, dy); } } @@ -747,68 +1031,38 @@ struct hb_font_t get_glyph_v_origin_with_fallback (glyph, x, y); } - void add_glyph_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) + void add_glyph_h_origins (hb_buffer_t *buf) { - hb_position_t origin_x, origin_y; - - get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x += origin_x; - *y += origin_y; + apply_glyph_h_origins_with_fallback (buf, +1); } - void add_glyph_v_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) + void add_glyph_v_origins (hb_buffer_t *buf) { - hb_position_t origin_x, origin_y; - - get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x += origin_x; - *y += origin_y; + apply_glyph_v_origins_with_fallback (buf, +1); } void add_glyph_origin_for_direction (hb_codepoint_t glyph, hb_direction_t direction, hb_position_t *x, hb_position_t *y) { hb_position_t origin_x, origin_y; - get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); - - *x += origin_x; - *y += origin_y; + add_offset (x, y, origin_x, origin_y); } - void subtract_glyph_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) + void subtract_glyph_h_origins (hb_buffer_t *buf) { - hb_position_t origin_x, origin_y; - - get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x -= origin_x; - *y -= origin_y; + apply_glyph_h_origins_with_fallback (buf, -1); } - void subtract_glyph_v_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) + void subtract_glyph_v_origins (hb_buffer_t *buf) { - hb_position_t origin_x, origin_y; - - get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x -= origin_x; - *y -= origin_y; + apply_glyph_v_origins_with_fallback (buf, -1); } void subtract_glyph_origin_for_direction (hb_codepoint_t glyph, hb_direction_t direction, hb_position_t *x, hb_position_t *y) { hb_position_t origin_x, origin_y; - get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); - - *x -= origin_x; - *y -= origin_y; + subtract_offset (x, y, origin_x, origin_y); } void get_glyph_kerning_for_direction (hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, @@ -890,11 +1144,6 @@ struct hb_font_t return false; } - bool is_synthetic () const - { - return x_embolden || y_embolden || slant; - } - void changed () { float upem = face->get_upem (); @@ -906,6 +1155,8 @@ struct hb_font_t bool y_neg = y_scale < 0; y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem; + is_synthetic = x_embolden || y_embolden || slant; + x_strength = roundf (abs (x_scale) * x_embolden); y_strength = roundf (abs (y_scale) * y_embolden); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-pool.hh b/src/java.desktop/share/native/libharfbuzz/hb-free-pool.hh similarity index 92% rename from src/java.desktop/share/native/libharfbuzz/hb-pool.hh rename to src/java.desktop/share/native/libharfbuzz/hb-free-pool.hh index 613c78d1973..3acd144a857 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-pool.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-free-pool.hh @@ -24,12 +24,12 @@ * Facebook Author(s): Behdad Esfahbod */ -#ifndef HB_POOL_HH -#define HB_POOL_HH +#ifndef HB_FREE_POOL_HH +#define HB_FREE_POOL_HH #include "hb.hh" -/* Memory pool for persistent allocation of small objects. +/* Memory pool for persistent alloc/free of small objects. * * Some AI musings on this, not necessarily true: * @@ -41,10 +41,10 @@ * sophisticated, use a real allocator. Or use a real language. */ template -struct hb_pool_t +struct hb_free_pool_t { - hb_pool_t () : next (nullptr) {} - ~hb_pool_t () + hb_free_pool_t () : next (nullptr) {} + ~hb_free_pool_t () { next = nullptr; @@ -104,4 +104,4 @@ struct hb_pool_t }; -#endif /* HB_POOL_HH */ +#endif /* HB_FREE_POOL_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc index e9c0e734a53..b90f966c57c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc @@ -143,6 +143,9 @@ _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) { + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return; + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; float x_mult = 1.f, y_mult = 1.f; @@ -184,12 +187,14 @@ static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face) FT_Set_Transform (ft_face, &matrix, nullptr); ft_font->transform = true; } + else + FT_Set_Transform (ft_face, nullptr, nullptr); #if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR) - unsigned int num_coords; - const float *coords = hb_font_get_var_coords_design (font, &num_coords); - if (num_coords) + if (font->has_nonzero_coords) { + unsigned int num_coords; + const float *coords = hb_font_get_var_coords_design (font, &num_coords); FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (num_coords, sizeof (FT_Fixed)); if (ft_coords) { @@ -199,6 +204,12 @@ static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face) hb_free (ft_coords); } } + else if (font->num_coords) + { + // Some old versions of FreeType crash if we + // call this function on non-variable fonts. + FT_Set_Var_Design_Coordinates (ft_face, 0, nullptr); + } #endif } @@ -1093,6 +1104,10 @@ _hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data FT_ULong length = 0; FT_Error error; + /* In new FreeType, a tag value of 1 loads the SFNT table directory. Reject it. */ + if (tag == 1) + return nullptr; + /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */ error = FT_Load_Sfnt_Table (ft_face, tag, 0, nullptr, &length); @@ -1366,7 +1381,7 @@ hb_ft_font_changed (hb_font_t *font) for (unsigned int i = 0; i < mm_var->num_axis; ++i) { - coords[i] = ft_coords[i] >>= 2; + coords[i] = (ft_coords[i] + 2) >> 2; nonzero = nonzero || coords[i]; } @@ -1717,7 +1732,12 @@ hb_ft_font_set_funcs (hb_font_t *font) ft_face->generic.finalizer = _release_blob; // And the FT_Library to the blob - hb_blob_set_user_data (blob, &ft_library_key, ft_library, destroy_ft_library, true); + if (unlikely (!hb_blob_set_user_data (blob, &ft_library_key, ft_library, destroy_ft_library, true))) + { + DEBUG_MSG (FT, font, "hb_blob_set_user_data() failed"); + FT_Done_Face (ft_face); + return; + } _hb_ft_font_set_funcs (font, ft_face, true); hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-geometry.hh b/src/java.desktop/share/native/libharfbuzz/hb-geometry.hh index 795f29f6ab0..0de062df68e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-geometry.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-geometry.hh @@ -26,7 +26,10 @@ #include "hb.hh" +#include "hb-algs.hh" + +template struct hb_extents_t { hb_extents_t () {} @@ -35,7 +38,7 @@ struct hb_extents_t ymin (hb_min (extents.y_bearing, extents.y_bearing + extents.height)), xmax (hb_max (extents.x_bearing, extents.x_bearing + extents.width)), ymax (hb_max (extents.y_bearing, extents.y_bearing + extents.height)) {} - hb_extents_t (float xmin, float ymin, float xmax, float ymax) : + hb_extents_t (Float xmin, Float ymin, Float xmax, Float ymax) : xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {} bool is_empty () const { return xmin >= xmax || ymin >= ymax; } @@ -69,7 +72,7 @@ struct hb_extents_t } void - add_point (float x, float y) + add_point (Float x, Float y) { if (unlikely (is_void ())) { @@ -97,62 +100,69 @@ struct hb_extents_t yneg ? y1 - y0 : y0 - y1}; } - float xmin = 0.f; - float ymin = 0.f; - float xmax = -1.f; - float ymax = -1.f; + Float xmin = 0; + Float ymin = 0; + Float xmax = -1; + Float ymax = -1; }; +template struct hb_transform_t { hb_transform_t () {} - hb_transform_t (float xx, float yx, - float xy, float yy, - float x0, float y0) : + hb_transform_t (Float xx, Float yx, + Float xy, Float yy, + Float x0, Float y0) : xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {} bool is_identity () const { - return xx == 1.f && yx == 0.f && - xy == 0.f && yy == 1.f && - x0 == 0.f && y0 == 0.f; + return xx == 1 && yx == 0 && + xy == 0 && yy == 1 && + x0 == 0 && y0 == 0; + } + bool is_translation () const + { + return xx == 1 && yx == 0 && + xy == 0 && yy == 1; } - void multiply (const hb_transform_t &o) + void multiply (const hb_transform_t &o, bool before=false) { - /* Copied from cairo, with "o" being "a" there and "this" being "b" there. */ - hb_transform_t r; - - r.xx = o.xx * xx + o.yx * xy; - r.yx = o.xx * yx + o.yx * yy; - - r.xy = o.xy * xx + o.yy * xy; - r.yy = o.xy * yx + o.yy * yy; - - r.x0 = o.x0 * xx + o.y0 * xy + x0; - r.y0 = o.x0 * yx + o.y0 * yy + y0; - - *this = r; + // Copied from cairo-matrix.c + const hb_transform_t &a = before ? o : *this; + const hb_transform_t &b = before ? *this : o; + *this = { + a.xx * b.xx + a.xy * b.yx, + a.yx * b.xx + a.yy * b.yx, + a.xx * b.xy + a.xy * b.yy, + a.yx * b.xy + a.yy * b.yy, + a.xx * b.x0 + a.xy * b.y0 + a.x0, + a.yx * b.x0 + a.yy * b.y0 + a.y0 + }; } - void transform_distance (float &dx, float &dy) const + HB_ALWAYS_INLINE + void transform_distance (Float &dx, Float &dy) const { - float new_x = xx * dx + xy * dy; - float new_y = yx * dx + yy * dy; + Float new_x = xx * dx + xy * dy; + Float new_y = yx * dx + yy * dy; dx = new_x; dy = new_y; } - void transform_point (float &x, float &y) const + HB_ALWAYS_INLINE + void transform_point (Float &x, Float &y) const { - transform_distance (x, y); - x += x0; - y += y0; + Float new_x = x0 + xx * x + xy * y; + Float new_y = y0 + yx * x + yy * y; + x = new_x; + y = new_y; } - void transform_extents (hb_extents_t &extents) const + void transform_extents (hb_extents_t &extents) const { - float quad_x[4], quad_y[4]; + Float quad_x[4], quad_y[4]; quad_x[0] = extents.xmin; quad_y[0] = extents.ymin; @@ -163,7 +173,7 @@ struct hb_transform_t quad_x[3] = extents.xmax; quad_y[3] = extents.ymax; - extents = hb_extents_t {}; + extents = hb_extents_t {}; for (unsigned i = 0; i < 4; i++) { transform_point (quad_x[i], quad_y[i]); @@ -171,20 +181,36 @@ struct hb_transform_t } } - void transform (const hb_transform_t &o) { multiply (o); } + void transform (const hb_transform_t &o, bool before=false) { multiply (o, before); } - void translate (float x, float y) + static hb_transform_t translation (Float x, Float y) { - if (x == 0.f && y == 0.f) - return; + return {1, 0, 0, 1, x, y}; + } + void translate (Float x, Float y, bool before=false) + { + if (before) + { + x0 += x; + y0 += y; + } + else + { + if (x == 0 && y == 0) + return; - x0 += xx * x + xy * y; - y0 += yx * x + yy * y; + x0 += xx * x + xy * y; + y0 += yx * x + yy * y; + } } - void scale (float scaleX, float scaleY) + static hb_transform_t scaling (Float scaleX, Float scaleY) { - if (scaleX == 1.f && scaleY == 1.f) + return {scaleX, 0, 0, scaleY, 0, 0}; + } + void scale (Float scaleX, Float scaleY) + { + if (scaleX == 1 && scaleY == 1) return; xx *= scaleX; @@ -192,52 +218,94 @@ struct hb_transform_t xy *= scaleY; yy *= scaleY; } - - void rotate (float rotation) + static hb_transform_t scaling_around_center (Float scaleX, Float scaleY, Float center_x, Float center_y) { - if (rotation == 0.f) + return {scaleX, 0, 0, scaleY, + center_x ? (1 - scaleX) * center_x : 0, + center_y ? (1 - scaleY) * center_y : 0}; + } + void scale_around_center (Float scaleX, Float scaleY, Float center_x, Float center_y) + { + if (scaleX == 1 && scaleY == 1) return; + transform (scaling_around_center (scaleX, scaleY, center_x, center_y)); + } + + static hb_transform_t rotation (Float radians) + { // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240 - rotation = rotation * HB_PI; - float c; - float s; -#ifdef HAVE_SINCOSF - sincosf (rotation, &s, &c); -#else - c = cosf (rotation); - s = sinf (rotation); -#endif - auto other = hb_transform_t{c, s, -s, c, 0.f, 0.f}; - transform (other); + Float c; + Float s; + hb_sincos (radians, s, c); + return {c, s, -s, c, 0, 0}; } - - void skew (float skewX, float skewY) + void rotate (Float radians, bool before=false) { - if (skewX == 0.f && skewY == 0.f) + if (radians == 0) return; - // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255 - skewX = skewX * HB_PI; - skewY = skewY * HB_PI; - auto other = hb_transform_t{1.f, - skewY ? tanf (skewY) : 0.f, - skewX ? tanf (skewX) : 0.f, - 1.f, - 0.f, 0.f}; - transform (other); + transform (rotation (radians), before); } - float xx = 1.f; - float yx = 0.f; - float xy = 0.f; - float yy = 1.f; - float x0 = 0.f; - float y0 = 0.f; + static hb_transform_t rotation_around_center (Float radians, Float center_x, Float center_y) + { + Float s, c; + hb_sincos (radians, s, c); + return { + c, s, -s, c, + (1 - c) * center_x + s * center_y, + -s * center_x + (1 - c) * center_y + }; + } + void rotate_around_center (Float radians, Float center_x, Float center_y, bool before=false) + { + if (radians == 0) + return; + + transform (rotation_around_center (radians, center_x, center_y), before); + } + + static hb_transform_t skewing (Float skewX, Float skewY) + { + return {1, skewY ? tanf (skewY) : 0, skewX ? tanf (skewX) : 0, 1, 0, 0}; + } + void skew (Float skewX, Float skewY) + { + if (skewX == 0 && skewY == 0) + return; + + transform (skewing (skewX, skewY)); + } + static hb_transform_t skewing_around_center (Float skewX, Float skewY, Float center_x, Float center_y) + { + skewX = skewX ? tanf (skewX) : 0; + skewY = skewY ? tanf (skewY) : 0; + return { + 1, skewY, skewX, 1, + center_y ? -skewX * center_y : 0, + center_x ? -skewY * center_x : 0 + }; + } + void skew_around_center (Float skewX, Float skewY, Float center_x, Float center_y) + { + if (skewX == 0 && skewY == 0) + return; + + transform (skewing_around_center (skewX, skewY, center_x, center_y)); + } + + Float xx = 1; + Float yx = 0; + Float xy = 0; + Float yy = 1; + Float x0 = 0; + Float y0 = 0; }; -#define HB_TRANSFORM_IDENTITY hb_transform_t{1.f, 0.f, 0.f, 1.f, 0.f, 0.f} +#define HB_TRANSFORM_IDENTITY {1, 0, 0, 1, 0, 0} +template struct hb_bounds_t { enum status_t { @@ -247,7 +315,7 @@ struct hb_bounds_t }; hb_bounds_t (status_t status = UNBOUNDED) : status (status) {} - hb_bounds_t (const hb_extents_t &extents) : + hb_bounds_t (const hb_extents_t &extents) : status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {} void union_ (const hb_bounds_t &o) @@ -281,20 +349,21 @@ struct hb_bounds_t } status_t status; - hb_extents_t extents; + hb_extents_t extents; }; +template struct hb_transform_decomposed_t { - float translateX = 0; - float translateY = 0; - float rotation = 0; // in degrees, counter-clockwise - float scaleX = 1; - float scaleY = 1; - float skewX = 0; // in degrees, counter-clockwise - float skewY = 0; // in degrees, counter-clockwise - float tCenterX = 0; - float tCenterY = 0; + Float translateX = 0; + Float translateY = 0; + Float rotation = 0; // in radians, counter-clockwise + Float scaleX = 1; + Float scaleY = 1; + Float skewX = 0; // in radians, counter-clockwise + Float skewY = 0; // in radians, counter-clockwise + Float tCenterX = 0; + Float tCenterY = 0; operator bool () const { @@ -305,9 +374,9 @@ struct hb_transform_decomposed_t tCenterX || tCenterY; } - hb_transform_t to_transform () const + hb_transform_t to_transform () const { - hb_transform_t t; + hb_transform_t t; t.translate (translateX + tCenterX, translateY + tCenterY); t.rotate (rotation); t.scale (scaleX, scaleY); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-iter.hh b/src/java.desktop/share/native/libharfbuzz/hb-iter.hh index 21d9544a74b..661e6771a4a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-iter.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-iter.hh @@ -772,8 +772,9 @@ struct hb_iota_iter_t : template auto inc (hb_type_identity s, hb_priority<1>) - -> hb_void_t (s), hb_declval ()))> - { v = hb_invoke (std::forward (s), v); } + -> hb_void_t> (s), + hb_declval ()))> + { v = hb_invoke (std::forward> (s), v); } void inc (S s, hb_priority<0>) @@ -972,7 +973,7 @@ struct Proj&& f = hb_identity) const { for (auto it = hb_iter (c); it; ++it) - if (!hb_match (std::forward (p), hb_get (std::forward (f), *it))) + if (!hb_match (p, hb_get (f, *it))) return false; return true; } @@ -989,7 +990,7 @@ struct Proj&& f = hb_identity) const { for (auto it = hb_iter (c); it; ++it) - if (hb_match (std::forward (p), hb_get (std::forward (f), *it))) + if (hb_match (p, hb_get (f, *it))) return true; return false; } @@ -1006,7 +1007,7 @@ struct Proj&& f = hb_identity) const { for (auto it = hb_iter (c); it; ++it) - if (hb_match (std::forward (p), hb_get (std::forward (f), *it))) + if (hb_match (p, hb_get (f, *it))) return false; return true; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-kern.hh b/src/java.desktop/share/native/libharfbuzz/hb-kern.hh index 1f2c8d5811b..cf08c16689f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-kern.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-kern.hh @@ -70,7 +70,7 @@ struct hb_kern_machine_t continue; } - skippy_iter.reset (idx); + skippy_iter.reset_fast (idx); unsigned unsafe_to; if (!skippy_iter.next (&unsafe_to)) { diff --git a/src/java.desktop/share/native/libharfbuzz/hb-limits.hh b/src/java.desktop/share/native/libharfbuzz/hb-limits.hh index fbc7bbe764e..857e183737f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-limits.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-limits.hh @@ -29,20 +29,20 @@ #ifndef HB_BUFFER_MAX_LEN_FACTOR -#define HB_BUFFER_MAX_LEN_FACTOR 64 +#define HB_BUFFER_MAX_LEN_FACTOR 256 #endif #ifndef HB_BUFFER_MAX_LEN_MIN -#define HB_BUFFER_MAX_LEN_MIN 16384 +#define HB_BUFFER_MAX_LEN_MIN 65536 #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 +#define HB_BUFFER_MAX_OPS_FACTOR 4096 #endif #ifndef HB_BUFFER_MAX_OPS_MIN -#define HB_BUFFER_MAX_OPS_MIN 16384 +#define HB_BUFFER_MAX_OPS_MIN 65536 #endif #ifndef HB_BUFFER_MAX_OPS_DEFAULT #define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh b/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh index ffbb8f3d301..8fa32ea8eaa 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh @@ -66,13 +66,22 @@ static inline Type& StructAtOffsetUnaligned(void *P, unsigned int offset) } /* StructAfter(X) returns the struct T& that is placed after X. - * Works with X of variable size also. X must implement get_size() */ -template -static inline const Type& StructAfter(const TObject &X) -{ return StructAtOffset(&X, X.get_size()); } -template -static inline Type& StructAfter(TObject &X) -{ return StructAtOffset(&X, X.get_size()); } + * Works with X of variable size also. X must implement get_size(). + * Any extra arguments are forwarded to get_size, so for example + * it can work with UnsizedArrayOf<> as well. */ +template +static inline auto StructAfter(const TObject &X, Ts... args) HB_AUTO_RETURN(( + StructAtOffset(&X, X.get_size(std::forward (args)...)) +)) +/* The is_const shenanigans is to avoid ambiguous overload with gcc-8. + * It disables this path when TObject is const. + * See: https://github.com/harfbuzz/harfbuzz/issues/5429 */ +template +static inline auto StructAfter(TObject &X, Ts... args) HB_AUTO_RETURN(( + sizeof(int[std::is_const::value ? -1 : +1]) > 0 ? + StructAtOffset(&X, X.get_size(std::forward (args)...)) + : *reinterpret_cast (0) +)) /* @@ -132,7 +141,6 @@ static inline Type& StructAfter(TObject &X) DEFINE_SIZE_ARRAY(size, array) - /* * Lazy loaders. * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-map.hh b/src/java.desktop/share/native/libharfbuzz/hb-map.hh index 76206342f57..4c76622df7a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-map.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-map.hh @@ -47,11 +47,11 @@ struct hb_hashmap_t hb_hashmap_t () { init (); } ~hb_hashmap_t () { fini (); } - hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () + void _copy (const hb_hashmap_t& o) { if (unlikely (!o.mask)) return; - if (item_t::is_trivial) + if (hb_is_trivially_copy_assignable (item_t)) { items = (item_t *) hb_malloc (sizeof (item_t) * (o.mask + 1)); if (unlikely (!items)) @@ -70,8 +70,16 @@ struct hb_hashmap_t alloc (o.population); hb_copy (o, *this); } + + hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { _copy (o); } + hb_hashmap_t& operator= (const hb_hashmap_t& o) + { + reset (); + if (!items) { _copy (o); return *this; } + alloc (o.population); hb_copy (o, *this); return *this; + } + hb_hashmap_t (hb_hashmap_t&& o) noexcept : hb_hashmap_t () { hb_swap (*this, o); } - hb_hashmap_t& operator= (const hb_hashmap_t& o) { reset (); alloc (o.population); hb_copy (o, *this); return *this; } hb_hashmap_t& operator= (hb_hashmap_t&& o) noexcept { hb_swap (*this, o); return *this; } hb_hashmap_t (std::initializer_list> lst) : hb_hashmap_t () @@ -130,10 +138,7 @@ struct hb_hashmap_t uint32_t total_hash () const { return (hash * 31u) + hb_hash (value); } - static constexpr bool is_trivial = hb_is_trivially_constructible(K) && - hb_is_trivially_destructible(K) && - hb_is_trivially_constructible(V) && - hb_is_trivially_destructible(V); + static constexpr bool is_trivially_constructible = (hb_is_trivially_constructible(K) && hb_is_trivially_constructible(V)); }; hb_object_header_t header; @@ -174,19 +179,19 @@ struct hb_hashmap_t if (likely (items)) { unsigned size = mask + 1; - if (!item_t::is_trivial) - for (unsigned i = 0; i < size; i++) - items[i].~item_t (); + for (unsigned i = 0; i < size; i++) + items[i].~item_t (); hb_free (items); items = nullptr; } population = occupancy = 0; } - void reset () + hb_hashmap_t& reset () { successful = true; clear (); + return *this; } bool in_error () const { return !successful; } @@ -197,7 +202,7 @@ struct hb_hashmap_t 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 power = hb_bit_storage (hb_max (hb_max ((unsigned) population, new_population) * 2, 4u)); 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)) @@ -205,7 +210,7 @@ struct hb_hashmap_t successful = false; return false; } - if (!item_t::is_trivial) + if (!item_t::is_trivially_constructible) for (auto &_ : hb_iter (new_items, new_size)) new (&_) item_t (); else @@ -231,9 +236,8 @@ struct hb_hashmap_t std::move (old_items[i].value)); } } - if (!item_t::is_trivial) - for (unsigned int i = 0; i < old_size; i++) - old_items[i].~item_t (); + for (unsigned int i = 0; i < old_size; i++) + old_items[i].~item_t (); hb_free (old_items); @@ -335,7 +339,13 @@ struct hb_hashmap_t bool has (const K &key, VV **vp = nullptr) const { if (!items) return false; - auto *item = fetch_item (key, hb_hash (key)); + return has_with_hash (key, hb_hash (key), vp); + } + template + bool has_with_hash (const K &key, uint32_t hash, VV **vp = nullptr) const + { + if (!items) return false; + auto *item = fetch_item (key, hash); if (item) { if (vp) *vp = std::addressof (item->value); @@ -481,10 +491,17 @@ struct hb_hashmap_t /* Sink interface. */ hb_hashmap_t& operator << (const hb_pair_t& v) { set (v.first, v.second); return *this; } + template hb_hashmap_t& operator << (const hb_pair_t& v) { set (v.first, std::move (v.second)); return *this; } + template hb_hashmap_t& operator << (const hb_pair_t& v) { set (std::move (v.first), v.second); return *this; } + template hb_hashmap_t& operator << (const hb_pair_t& v) { set (std::move (v.first), std::move (v.second)); return *this; } 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-open-file.hh b/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh index 382ee2ddaf2..538240b6416 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh @@ -465,11 +465,11 @@ struct OpenTypeFontFile Typ1Tag = HB_TAG ('t','y','p','1') /* Obsolete Apple Type1 font in SFNT container */ }; - hb_tag_t get_tag () const { return u.tag; } + hb_tag_t get_tag () const { return u.tag.v; } unsigned int get_face_count () const { - switch (u.tag) { + switch (u.tag.v) { case CFFTag: /* All the non-collection tags */ case TrueTag: case Typ1Tag: @@ -483,7 +483,7 @@ struct OpenTypeFontFile { if (base_offset) *base_offset = 0; - switch (u.tag) { + switch (u.tag.v) { /* Note: for non-collection SFNT data we ignore index. This is because * Apple dfont container is a container of SFNT's. So each SFNT is a * non-TTC, but the index is more than zero. */ @@ -512,9 +512,9 @@ struct OpenTypeFontFile bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (unlikely (!u.tag.sanitize (c))) return_trace (false); + if (unlikely (!u.tag.v.sanitize (c))) return_trace (false); hb_barrier (); - switch (u.tag) { + switch (u.tag.v) { case CFFTag: /* All the non-collection tags */ case TrueTag: case Typ1Tag: @@ -527,13 +527,13 @@ struct OpenTypeFontFile protected: union { - Tag tag; /* 4-byte identifier. */ + struct { Tag v; } tag; /* 4-byte identifier. */ OpenTypeFontFace fontFace; TTCHeader ttcHeader; ResourceForkHeader rfHeader; } u; public: - DEFINE_SIZE_UNION (4, tag); + DEFINE_SIZE_UNION (4, tag.v); }; 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 75d87f11ea5..ad831b66d56 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh @@ -54,35 +54,41 @@ namespace OT { */ /* Integer types in big-endian order and no alignment requirement */ -template -struct IntType +struct NumType { typedef Type type; - - IntType () = default; - explicit constexpr IntType (Type V) : v {V} {} - IntType& operator = (Type i) { v = i; return *this; } /* For reason we define cast out operator for signed/unsigned, instead of Type, see: * https://github.com/harfbuzz/harfbuzz/pull/2875/commits/09836013995cab2b9f07577a179ad7b024130467 */ - operator typename std::conditional::value, signed, unsigned>::type () const { return v; } + typedef typename std::conditional::value && sizeof (Type) <= sizeof(int), + typename std::conditional::value, signed, unsigned>::type, + Type>::type WideType; - bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; } - bool operator != (const IntType &o) const { return !(*this == o); } + NumType () = default; + explicit constexpr NumType (Type V) : v {V} {} + NumType& operator = (Type V) { v = V; return *this; } - IntType& operator += (unsigned count) { *this = *this + count; return *this; } - IntType& operator -= (unsigned count) { *this = *this - count; return *this; } - IntType& operator ++ () { *this += 1; return *this; } - IntType& operator -- () { *this -= 1; return *this; } - IntType operator ++ (int) { IntType c (*this); ++*this; return c; } - IntType operator -- (int) { IntType c (*this); --*this; return c; } + operator WideType () const { return v; } - HB_INTERNAL static int cmp (const IntType *a, const IntType *b) + bool operator == (const NumType &o) const { return (Type) v == (Type) o.v; } + bool operator != (const NumType &o) const { return !(*this == o); } + + NumType& operator += (WideType count) { *this = *this + count; return *this; } + NumType& operator -= (WideType count) { *this = *this - count; return *this; } + NumType& operator ++ () { *this += 1; return *this; } + NumType& operator -- () { *this -= 1; return *this; } + NumType operator ++ (int) { NumType c (*this); ++*this; return c; } + NumType operator -- (int) { NumType c (*this); --*this; return c; } + + uint32_t hash () const { return hb_array ((const char *) &v, sizeof (v)).hash (); } + HB_INTERNAL static int cmp (const NumType *a, const NumType *b) { return b->cmp (*a); } HB_INTERNAL static int cmp (const void *a, const void *b) { - IntType *pa = (IntType *) a; - IntType *pb = (IntType *) b; + NumType *pa = (NumType *) a; + NumType *pb = (NumType *) b; return pb->cmp (*pa); } @@ -99,20 +105,36 @@ struct IntType return_trace (c->check_struct (this)); } protected: - BEInt v; + typename std::conditional::value, + HBInt, + HBFloat>::type v; public: DEFINE_SIZE_STATIC (Size); }; -typedef IntType HBUINT8; /* 8-bit unsigned integer. */ -typedef IntType HBINT8; /* 8-bit signed integer. */ -typedef IntType HBUINT16; /* 16-bit unsigned integer. */ -typedef IntType HBINT16; /* 16-bit signed integer. */ -typedef IntType HBUINT32; /* 32-bit unsigned integer. */ -typedef IntType HBINT32; /* 32-bit signed integer. */ +typedef NumType HBUINT8; /* 8-bit big-endian unsigned integer. */ +typedef NumType HBINT8; /* 8-bit big-endian signed integer. */ +typedef NumType HBUINT16; /* 16-bit big-endian unsigned integer. */ +typedef NumType HBINT16; /* 16-bit big-endian signed integer. */ +typedef NumType HBUINT32; /* 32-bit big-endian unsigned integer. */ +typedef NumType HBINT32; /* 32-bit big-endian signed integer. */ +typedef NumType HBUINT64; /* 64-bit big-endian unsigned integer. */ +typedef NumType HBINT64; /* 64-bit big-endian signed integer. */ /* Note: we cannot defined a signed HBINT24 because there's no corresponding C type. * Works for unsigned, but not signed, since we rely on compiler for sign-extension. */ -typedef IntType HBUINT24; /* 24-bit unsigned integer. */ +typedef NumType HBUINT24; /* 24-bit big-endian unsigned integer. */ + +typedef NumType HBUINT16LE; /* 16-bit little-endian unsigned integer. */ +typedef NumType HBINT16LE; /* 16-bit little-endian signed integer. */ +typedef NumType HBUINT32LE; /* 32-bit little-endian unsigned integer. */ +typedef NumType HBINT32LE; /* 32-bit little-endian signed integer. */ +typedef NumType HBUINT64LE; /* 64-bit little-endian unsigned integer. */ +typedef NumType HBINT64LE; /* 64-bit little-endian signed integer. */ + +typedef NumType HBFLOAT32BE; /* 32-bit little-endian floating point number. */ +typedef NumType HBFLOAT64BE; /* 64-bit little-endian floating point number. */ +typedef NumType HBFLOAT32LE; /* 32-bit little-endian floating point number. */ +typedef NumType HBFLOAT64LE; /* 64-bit little-endian floating point number. */ /* 15-bit unsigned number; top bit used for extension. */ struct HBUINT15 : HBUINT16 @@ -218,7 +240,7 @@ typedef HBUINT16 UFWORD; template struct HBFixed : Type { - static constexpr float shift = (float) (1 << fraction_bits); + static constexpr float mult = 1.f / (1 << fraction_bits); static_assert (Type::static_size * 8 > fraction_bits, ""); operator signed () const = delete; @@ -226,8 +248,8 @@ struct HBFixed : Type explicit operator float () const { return to_float (); } 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); } + float to_float (float offset = 0) const { return ((int32_t) Type::v + offset) * mult; } + void set_float (float f) { Type::v = roundf (f / mult); } public: DEFINE_SIZE_STATIC (Type::static_size); }; @@ -504,16 +526,9 @@ struct OffsetTo : Offset return_trace (sanitize_shallow (c, base) && hb_barrier () && (this->is_null () || - c->dispatch (StructAtOffset (base, *this), std::forward (ds)...) || - neuter (c))); + c->dispatch (StructAtOffset (base, *this), std::forward (ds)...))); } - /* Set the offset to Null */ - bool neuter (hb_sanitize_context_t *c) const - { - if (!has_null) return false; - return c->try_set (this, 0); - } DEFINE_SIZE_STATIC (sizeof (OffsetType)); }; /* Partial specializations. */ @@ -1481,8 +1496,8 @@ struct TupleValues VALUE_RUN_COUNT_MASK = 0x3F }; - static unsigned compile (hb_array_t values, /* IN */ - hb_array_t encoded_bytes /* OUT */) + static unsigned compile_unsafe (hb_array_t values, /* IN */ + unsigned char *encoded_bytes /* OUT */) { unsigned num_values = values.length; unsigned encoded_len = 0; @@ -1491,24 +1506,23 @@ struct TupleValues { int val = values.arrayZ[i]; if (val == 0) - encoded_len += encode_value_run_as_zeroes (i, encoded_bytes.sub_array (encoded_len), values); - else if (val >= -128 && val <= 127) - encoded_len += encode_value_run_as_bytes (i, encoded_bytes.sub_array (encoded_len), values); - else if (val >= -32768 && val <= 32767) - encoded_len += encode_value_run_as_words (i, encoded_bytes.sub_array (encoded_len), values); + encoded_len += encode_value_run_as_zeroes (i, encoded_bytes + encoded_len, values); + else if ((int8_t) val == val) + encoded_len += encode_value_run_as_bytes (i, encoded_bytes + encoded_len, values); + else if ((int16_t) val == val) + encoded_len += encode_value_run_as_words (i, encoded_bytes + encoded_len, values); else - encoded_len += encode_value_run_as_longs (i, encoded_bytes.sub_array (encoded_len), values); + encoded_len += encode_value_run_as_longs (i, encoded_bytes + encoded_len, values); } return encoded_len; } static unsigned encode_value_run_as_zeroes (unsigned& i, - hb_array_t encoded_bytes, + unsigned char *it, hb_array_t values) { unsigned num_values = values.length; unsigned run_length = 0; - auto it = encoded_bytes.iter (); unsigned encoded_len = 0; while (i < num_values && values.arrayZ[i] == 0) { @@ -1532,7 +1546,7 @@ struct TupleValues } static unsigned encode_value_run_as_bytes (unsigned &i, - hb_array_t encoded_bytes, + unsigned char *it, hb_array_t values) { unsigned start = i; @@ -1540,7 +1554,7 @@ struct TupleValues while (i < num_values) { int val = values.arrayZ[i]; - if (val > 127 || val < -128) + if ((int8_t) val != val) break; /* from fonttools: if there're 2 or more zeros in a sequence, @@ -1553,7 +1567,6 @@ struct TupleValues unsigned run_length = i - start; unsigned encoded_len = 0; - auto it = encoded_bytes.iter (); while (run_length >= 64) { @@ -1561,10 +1574,9 @@ struct TupleValues encoded_len++; for (unsigned j = 0; j < 64; j++) - { - *it++ = static_cast (values.arrayZ[start + j]); - encoded_len++; - } + it[j] = static_cast (values.arrayZ[start + j]); + it += 64; + encoded_len += 64; start += 64; run_length -= 64; @@ -1575,18 +1587,16 @@ struct TupleValues *it++ = (VALUES_ARE_BYTES | (run_length - 1)); encoded_len++; - while (start < i) - { - *it++ = static_cast (values.arrayZ[start++]); - encoded_len++; - } + for (unsigned j = 0; j < run_length; j++) + it[j] = static_cast (values.arrayZ[start + j]); + encoded_len += run_length; } return encoded_len; } static unsigned encode_value_run_as_words (unsigned &i, - hb_array_t encoded_bytes, + unsigned char *it, hb_array_t values) { unsigned start = i; @@ -1595,22 +1605,24 @@ struct TupleValues { int val = values.arrayZ[i]; - /* start a new run for a single zero value*/ + if ((int16_t) val != val) + break; + + /* start a new run for a single zero value. */ if (val == 0) break; - /* from fonttools: continue word-encoded run if there's only one + /* From fonttools: continue word-encoded run if there's only one * single value in the range [-128, 127] because it is more compact. * Only start a new run when there're 2 continuous such values. */ - if (val >= -128 && val <= 127 && + if ((int8_t) val == val && i + 1 < num_values && - values.arrayZ[i+1] >= -128 && values.arrayZ[i+1] <= 127) + (int8_t) values.arrayZ[i+1] == values.arrayZ[i+1]) break; i++; } unsigned run_length = i - start; - auto it = encoded_bytes.iter (); unsigned encoded_len = 0; while (run_length >= 64) { @@ -1647,7 +1659,7 @@ struct TupleValues } static unsigned encode_value_run_as_longs (unsigned &i, - hb_array_t encoded_bytes, + unsigned char *it, hb_array_t values) { unsigned start = i; @@ -1656,14 +1668,13 @@ struct TupleValues { int val = values.arrayZ[i]; - if (val >= -32768 && val <= 32767) + if ((int16_t) val == val) break; i++; } unsigned run_length = i - start; - auto it = encoded_bytes.iter (); unsigned encoded_len = 0; while (run_length >= 64) { @@ -1704,10 +1715,14 @@ struct TupleValues } template +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif static bool decompile (const HBUINT8 *&p /* IN/OUT */, hb_vector_t &values /* IN/OUT */, const HBUINT8 *end, - bool consume_all = false) + bool consume_all = false, + unsigned start = 0) { unsigned i = 0; unsigned count = consume_all ? UINT_MAX : values.length; @@ -1720,19 +1735,24 @@ struct TupleValues unsigned run_count = (control & VALUE_RUN_COUNT_MASK) + 1; if (consume_all) { - if (unlikely (!values.resize (values.length + run_count, false))) + if (unlikely (!values.resize_dirty (values.length + run_count))) return false; } unsigned stop = i + run_count; if (unlikely (stop > count)) return false; + + unsigned skip = i < start ? hb_min (start - i, run_count) : 0; + i += skip; + if ((control & VALUES_SIZE_MASK) == VALUES_ARE_ZEROS) { - for (; i < stop; i++) - values.arrayZ[i] = 0; + hb_memset (&values.arrayZ[i], 0, (stop - i) * sizeof (T)); + i = stop; } else if ((control & VALUES_SIZE_MASK) == VALUES_ARE_WORDS) { if (unlikely (p + run_count * HBINT16::static_size > end)) return false; + p += skip * HBINT16::static_size; #ifndef HB_OPTIMIZE_SIZE for (; i + 3 < stop; i += 4) { @@ -1755,6 +1775,7 @@ struct TupleValues else if ((control & VALUES_SIZE_MASK) == VALUES_ARE_LONGS) { if (unlikely (p + run_count * HBINT32::static_size > end)) return false; + p += skip * HBINT32::static_size; for (; i < stop; i++) { values.arrayZ[i] = * (const HBINT32 *) p; @@ -1764,6 +1785,7 @@ struct TupleValues else if ((control & VALUES_SIZE_MASK) == VALUES_ARE_BYTES) { if (unlikely (p + run_count > end)) return false; + p += skip * HBINT8::static_size; #ifndef HB_OPTIMIZE_SIZE for (; i + 3 < stop; i += 4) { @@ -1784,7 +1806,7 @@ struct TupleValues { iter_t (const unsigned char *p_, unsigned len_) : p (p_), endp (p_ + len_) - { if (ensure_run ()) read_value (); } + { if (likely (ensure_run ())) read_value (); } private: const unsigned char *p; @@ -1793,10 +1815,14 @@ struct TupleValues signed run_count = 0; unsigned width = 0; + HB_ALWAYS_INLINE bool ensure_run () { if (likely (run_count > 0)) return true; - + return _ensure_run (); + } + bool _ensure_run () + { if (unlikely (p >= endp)) { run_count = 0; @@ -1886,10 +1912,15 @@ struct TupleValues signed run_count = 0; unsigned width = 0; + HB_ALWAYS_INLINE bool ensure_run () { - if (run_count > 0) return true; + if (likely (run_count > 0)) return true; + return _ensure_run (); + } + bool _ensure_run () + { if (unlikely (p >= end)) { run_count = 0; @@ -2013,7 +2044,10 @@ struct TupleValues } #ifndef HB_OPTIMIZE_SIZE - if (scale == 1.0f) + // The following branch is supposed to speed things up by avoiding + // the multiplication in _add_to<> if scale is 1.0f. + // But in practice it seems to bloat the code and slow things down. + if (false && scale == 1.0f) _add_to (out); else #endif @@ -2038,6 +2072,23 @@ struct TupleList : CFF2Index }; +// Alignment + +template +struct Align +{ + unsigned get_size (const void *base) const + { + unsigned offset = (const char *) this - (const char *) base; + return (alignment - offset) & (alignment - 1); + } + + public: + DEFINE_SIZE_MIN (0); +}; + + + } /* namespace OT */ 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 1e3e0be5106..ec018efe645 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 @@ -79,7 +79,7 @@ struct Dict : UnsizedByteStr { TRACE_SERIALIZE (this); for (unsigned int i = 0; i < dictval.get_count (); i++) - if (unlikely (!opszr.serialize (c, dictval[i], std::forward (ds)...))) + if (unlikely (!opszr.serialize (c, dictval[i], ds...))) return_trace (false); return_trace (true); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-std-str.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-std-str.hh index 65d56ae18b5..bf56abb975c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-std-str.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-std-str.hh @@ -30,396 +30,396 @@ #include "hb.hh" #endif -_S(".notdef") -_S("space") -_S("exclam") -_S("quotedbl") -_S("numbersign") -_S("dollar") -_S("percent") -_S("ampersand") -_S("quoteright") -_S("parenleft") -_S("parenright") -_S("asterisk") -_S("plus") -_S("comma") -_S("hyphen") -_S("period") -_S("slash") -_S("zero") -_S("one") -_S("two") -_S("three") -_S("four") -_S("five") -_S("six") -_S("seven") -_S("eight") -_S("nine") -_S("colon") -_S("semicolon") -_S("less") -_S("equal") -_S("greater") -_S("question") -_S("at") -_S("A") -_S("B") -_S("C") -_S("D") -_S("E") -_S("F") -_S("G") -_S("H") -_S("I") -_S("J") -_S("K") -_S("L") -_S("M") -_S("N") -_S("O") -_S("P") -_S("Q") -_S("R") -_S("S") -_S("T") -_S("U") -_S("V") -_S("W") -_S("X") -_S("Y") -_S("Z") -_S("bracketleft") -_S("backslash") -_S("bracketright") -_S("asciicircum") -_S("underscore") -_S("quoteleft") -_S("a") -_S("b") -_S("c") -_S("d") -_S("e") -_S("f") -_S("g") -_S("h") -_S("i") -_S("j") -_S("k") -_S("l") -_S("m") -_S("n") -_S("o") -_S("p") -_S("q") -_S("r") -_S("s") -_S("t") -_S("u") -_S("v") -_S("w") -_S("x") -_S("y") -_S("z") -_S("braceleft") -_S("bar") -_S("braceright") -_S("asciitilde") -_S("exclamdown") -_S("cent") -_S("sterling") -_S("fraction") -_S("yen") -_S("florin") -_S("section") -_S("currency") -_S("quotesingle") -_S("quotedblleft") -_S("guillemotleft") -_S("guilsinglleft") -_S("guilsinglright") -_S("fi") -_S("fl") -_S("endash") -_S("dagger") -_S("daggerdbl") -_S("periodcentered") -_S("paragraph") -_S("bullet") -_S("quotesinglbase") -_S("quotedblbase") -_S("quotedblright") -_S("guillemotright") -_S("ellipsis") -_S("perthousand") -_S("questiondown") -_S("grave") -_S("acute") -_S("circumflex") -_S("tilde") -_S("macron") -_S("breve") -_S("dotaccent") -_S("dieresis") -_S("ring") -_S("cedilla") -_S("hungarumlaut") -_S("ogonek") -_S("caron") -_S("emdash") -_S("AE") -_S("ordfeminine") -_S("Lslash") -_S("Oslash") -_S("OE") -_S("ordmasculine") -_S("ae") -_S("dotlessi") -_S("lslash") -_S("oslash") -_S("oe") -_S("germandbls") -_S("onesuperior") -_S("logicalnot") -_S("mu") -_S("trademark") -_S("Eth") -_S("onehalf") -_S("plusminus") -_S("Thorn") -_S("onequarter") -_S("divide") -_S("brokenbar") -_S("degree") -_S("thorn") -_S("threequarters") -_S("twosuperior") -_S("registered") -_S("minus") -_S("eth") -_S("multiply") -_S("threesuperior") -_S("copyright") -_S("Aacute") -_S("Acircumflex") -_S("Adieresis") -_S("Agrave") -_S("Aring") -_S("Atilde") -_S("Ccedilla") -_S("Eacute") -_S("Ecircumflex") -_S("Edieresis") -_S("Egrave") -_S("Iacute") -_S("Icircumflex") -_S("Idieresis") -_S("Igrave") -_S("Ntilde") -_S("Oacute") -_S("Ocircumflex") -_S("Odieresis") -_S("Ograve") -_S("Otilde") -_S("Scaron") -_S("Uacute") -_S("Ucircumflex") -_S("Udieresis") -_S("Ugrave") -_S("Yacute") -_S("Ydieresis") -_S("Zcaron") -_S("aacute") -_S("acircumflex") -_S("adieresis") -_S("agrave") -_S("aring") -_S("atilde") -_S("ccedilla") -_S("eacute") -_S("ecircumflex") -_S("edieresis") -_S("egrave") -_S("iacute") -_S("icircumflex") -_S("idieresis") -_S("igrave") -_S("ntilde") -_S("oacute") -_S("ocircumflex") -_S("odieresis") -_S("ograve") -_S("otilde") -_S("scaron") -_S("uacute") -_S("ucircumflex") -_S("udieresis") -_S("ugrave") -_S("yacute") -_S("ydieresis") -_S("zcaron") -_S("exclamsmall") -_S("Hungarumlautsmall") -_S("dollaroldstyle") -_S("dollarsuperior") -_S("ampersandsmall") -_S("Acutesmall") -_S("parenleftsuperior") -_S("parenrightsuperior") -_S("twodotenleader") -_S("onedotenleader") -_S("zerooldstyle") -_S("oneoldstyle") -_S("twooldstyle") -_S("threeoldstyle") -_S("fouroldstyle") -_S("fiveoldstyle") -_S("sixoldstyle") -_S("sevenoldstyle") -_S("eightoldstyle") -_S("nineoldstyle") -_S("commasuperior") -_S("threequartersemdash") -_S("periodsuperior") -_S("questionsmall") -_S("asuperior") -_S("bsuperior") -_S("centsuperior") -_S("dsuperior") -_S("esuperior") -_S("isuperior") -_S("lsuperior") -_S("msuperior") -_S("nsuperior") -_S("osuperior") -_S("rsuperior") -_S("ssuperior") -_S("tsuperior") -_S("ff") -_S("ffi") -_S("ffl") -_S("parenleftinferior") -_S("parenrightinferior") -_S("Circumflexsmall") -_S("hyphensuperior") -_S("Gravesmall") -_S("Asmall") -_S("Bsmall") -_S("Csmall") -_S("Dsmall") -_S("Esmall") -_S("Fsmall") -_S("Gsmall") -_S("Hsmall") -_S("Ismall") -_S("Jsmall") -_S("Ksmall") -_S("Lsmall") -_S("Msmall") -_S("Nsmall") -_S("Osmall") -_S("Psmall") -_S("Qsmall") -_S("Rsmall") -_S("Ssmall") -_S("Tsmall") -_S("Usmall") -_S("Vsmall") -_S("Wsmall") -_S("Xsmall") -_S("Ysmall") -_S("Zsmall") -_S("colonmonetary") -_S("onefitted") -_S("rupiah") -_S("Tildesmall") -_S("exclamdownsmall") -_S("centoldstyle") -_S("Lslashsmall") -_S("Scaronsmall") -_S("Zcaronsmall") -_S("Dieresissmall") -_S("Brevesmall") -_S("Caronsmall") -_S("Dotaccentsmall") -_S("Macronsmall") -_S("figuredash") -_S("hypheninferior") -_S("Ogoneksmall") -_S("Ringsmall") -_S("Cedillasmall") -_S("questiondownsmall") -_S("oneeighth") -_S("threeeighths") -_S("fiveeighths") -_S("seveneighths") -_S("onethird") -_S("twothirds") -_S("zerosuperior") -_S("foursuperior") -_S("fivesuperior") -_S("sixsuperior") -_S("sevensuperior") -_S("eightsuperior") -_S("ninesuperior") -_S("zeroinferior") -_S("oneinferior") -_S("twoinferior") -_S("threeinferior") -_S("fourinferior") -_S("fiveinferior") -_S("sixinferior") -_S("seveninferior") -_S("eightinferior") -_S("nineinferior") -_S("centinferior") -_S("dollarinferior") -_S("periodinferior") -_S("commainferior") -_S("Agravesmall") -_S("Aacutesmall") -_S("Acircumflexsmall") -_S("Atildesmall") -_S("Adieresissmall") -_S("Aringsmall") -_S("AEsmall") -_S("Ccedillasmall") -_S("Egravesmall") -_S("Eacutesmall") -_S("Ecircumflexsmall") -_S("Edieresissmall") -_S("Igravesmall") -_S("Iacutesmall") -_S("Icircumflexsmall") -_S("Idieresissmall") -_S("Ethsmall") -_S("Ntildesmall") -_S("Ogravesmall") -_S("Oacutesmall") -_S("Ocircumflexsmall") -_S("Otildesmall") -_S("Odieresissmall") -_S("OEsmall") -_S("Oslashsmall") -_S("Ugravesmall") -_S("Uacutesmall") -_S("Ucircumflexsmall") -_S("Udieresissmall") -_S("Yacutesmall") -_S("Thornsmall") -_S("Ydieresissmall") -_S("001.000") -_S("001.001") -_S("001.002") -_S("001.003") -_S("Black") -_S("Bold") -_S("Book") -_S("Light") -_S("Medium") -_S("Regular") -_S("Roman") -_S("Semibold") +HB_STR(".notdef") +HB_STR("space") +HB_STR("exclam") +HB_STR("quotedbl") +HB_STR("numbersign") +HB_STR("dollar") +HB_STR("percent") +HB_STR("ampersand") +HB_STR("quoteright") +HB_STR("parenleft") +HB_STR("parenright") +HB_STR("asterisk") +HB_STR("plus") +HB_STR("comma") +HB_STR("hyphen") +HB_STR("period") +HB_STR("slash") +HB_STR("zero") +HB_STR("one") +HB_STR("two") +HB_STR("three") +HB_STR("four") +HB_STR("five") +HB_STR("six") +HB_STR("seven") +HB_STR("eight") +HB_STR("nine") +HB_STR("colon") +HB_STR("semicolon") +HB_STR("less") +HB_STR("equal") +HB_STR("greater") +HB_STR("question") +HB_STR("at") +HB_STR("A") +HB_STR("B") +HB_STR("C") +HB_STR("D") +HB_STR("E") +HB_STR("F") +HB_STR("G") +HB_STR("H") +HB_STR("I") +HB_STR("J") +HB_STR("K") +HB_STR("L") +HB_STR("M") +HB_STR("N") +HB_STR("O") +HB_STR("P") +HB_STR("Q") +HB_STR("R") +HB_STR("S") +HB_STR("T") +HB_STR("U") +HB_STR("V") +HB_STR("W") +HB_STR("X") +HB_STR("Y") +HB_STR("Z") +HB_STR("bracketleft") +HB_STR("backslash") +HB_STR("bracketright") +HB_STR("asciicircum") +HB_STR("underscore") +HB_STR("quoteleft") +HB_STR("a") +HB_STR("b") +HB_STR("c") +HB_STR("d") +HB_STR("e") +HB_STR("f") +HB_STR("g") +HB_STR("h") +HB_STR("i") +HB_STR("j") +HB_STR("k") +HB_STR("l") +HB_STR("m") +HB_STR("n") +HB_STR("o") +HB_STR("p") +HB_STR("q") +HB_STR("r") +HB_STR("s") +HB_STR("t") +HB_STR("u") +HB_STR("v") +HB_STR("w") +HB_STR("x") +HB_STR("y") +HB_STR("z") +HB_STR("braceleft") +HB_STR("bar") +HB_STR("braceright") +HB_STR("asciitilde") +HB_STR("exclamdown") +HB_STR("cent") +HB_STR("sterling") +HB_STR("fraction") +HB_STR("yen") +HB_STR("florin") +HB_STR("section") +HB_STR("currency") +HB_STR("quotesingle") +HB_STR("quotedblleft") +HB_STR("guillemotleft") +HB_STR("guilsinglleft") +HB_STR("guilsinglright") +HB_STR("fi") +HB_STR("fl") +HB_STR("endash") +HB_STR("dagger") +HB_STR("daggerdbl") +HB_STR("periodcentered") +HB_STR("paragraph") +HB_STR("bullet") +HB_STR("quotesinglbase") +HB_STR("quotedblbase") +HB_STR("quotedblright") +HB_STR("guillemotright") +HB_STR("ellipsis") +HB_STR("perthousand") +HB_STR("questiondown") +HB_STR("grave") +HB_STR("acute") +HB_STR("circumflex") +HB_STR("tilde") +HB_STR("macron") +HB_STR("breve") +HB_STR("dotaccent") +HB_STR("dieresis") +HB_STR("ring") +HB_STR("cedilla") +HB_STR("hungarumlaut") +HB_STR("ogonek") +HB_STR("caron") +HB_STR("emdash") +HB_STR("AE") +HB_STR("ordfeminine") +HB_STR("Lslash") +HB_STR("Oslash") +HB_STR("OE") +HB_STR("ordmasculine") +HB_STR("ae") +HB_STR("dotlessi") +HB_STR("lslash") +HB_STR("oslash") +HB_STR("oe") +HB_STR("germandbls") +HB_STR("onesuperior") +HB_STR("logicalnot") +HB_STR("mu") +HB_STR("trademark") +HB_STR("Eth") +HB_STR("onehalf") +HB_STR("plusminus") +HB_STR("Thorn") +HB_STR("onequarter") +HB_STR("divide") +HB_STR("brokenbar") +HB_STR("degree") +HB_STR("thorn") +HB_STR("threequarters") +HB_STR("twosuperior") +HB_STR("registered") +HB_STR("minus") +HB_STR("eth") +HB_STR("multiply") +HB_STR("threesuperior") +HB_STR("copyright") +HB_STR("Aacute") +HB_STR("Acircumflex") +HB_STR("Adieresis") +HB_STR("Agrave") +HB_STR("Aring") +HB_STR("Atilde") +HB_STR("Ccedilla") +HB_STR("Eacute") +HB_STR("Ecircumflex") +HB_STR("Edieresis") +HB_STR("Egrave") +HB_STR("Iacute") +HB_STR("Icircumflex") +HB_STR("Idieresis") +HB_STR("Igrave") +HB_STR("Ntilde") +HB_STR("Oacute") +HB_STR("Ocircumflex") +HB_STR("Odieresis") +HB_STR("Ograve") +HB_STR("Otilde") +HB_STR("Scaron") +HB_STR("Uacute") +HB_STR("Ucircumflex") +HB_STR("Udieresis") +HB_STR("Ugrave") +HB_STR("Yacute") +HB_STR("Ydieresis") +HB_STR("Zcaron") +HB_STR("aacute") +HB_STR("acircumflex") +HB_STR("adieresis") +HB_STR("agrave") +HB_STR("aring") +HB_STR("atilde") +HB_STR("ccedilla") +HB_STR("eacute") +HB_STR("ecircumflex") +HB_STR("edieresis") +HB_STR("egrave") +HB_STR("iacute") +HB_STR("icircumflex") +HB_STR("idieresis") +HB_STR("igrave") +HB_STR("ntilde") +HB_STR("oacute") +HB_STR("ocircumflex") +HB_STR("odieresis") +HB_STR("ograve") +HB_STR("otilde") +HB_STR("scaron") +HB_STR("uacute") +HB_STR("ucircumflex") +HB_STR("udieresis") +HB_STR("ugrave") +HB_STR("yacute") +HB_STR("ydieresis") +HB_STR("zcaron") +HB_STR("exclamsmall") +HB_STR("Hungarumlautsmall") +HB_STR("dollaroldstyle") +HB_STR("dollarsuperior") +HB_STR("ampersandsmall") +HB_STR("Acutesmall") +HB_STR("parenleftsuperior") +HB_STR("parenrightsuperior") +HB_STR("twodotenleader") +HB_STR("onedotenleader") +HB_STR("zerooldstyle") +HB_STR("oneoldstyle") +HB_STR("twooldstyle") +HB_STR("threeoldstyle") +HB_STR("fouroldstyle") +HB_STR("fiveoldstyle") +HB_STR("sixoldstyle") +HB_STR("sevenoldstyle") +HB_STR("eightoldstyle") +HB_STR("nineoldstyle") +HB_STR("commasuperior") +HB_STR("threequartersemdash") +HB_STR("periodsuperior") +HB_STR("questionsmall") +HB_STR("asuperior") +HB_STR("bsuperior") +HB_STR("centsuperior") +HB_STR("dsuperior") +HB_STR("esuperior") +HB_STR("isuperior") +HB_STR("lsuperior") +HB_STR("msuperior") +HB_STR("nsuperior") +HB_STR("osuperior") +HB_STR("rsuperior") +HB_STR("ssuperior") +HB_STR("tsuperior") +HB_STR("ff") +HB_STR("ffi") +HB_STR("ffl") +HB_STR("parenleftinferior") +HB_STR("parenrightinferior") +HB_STR("Circumflexsmall") +HB_STR("hyphensuperior") +HB_STR("Gravesmall") +HB_STR("Asmall") +HB_STR("Bsmall") +HB_STR("Csmall") +HB_STR("Dsmall") +HB_STR("Esmall") +HB_STR("Fsmall") +HB_STR("Gsmall") +HB_STR("Hsmall") +HB_STR("Ismall") +HB_STR("Jsmall") +HB_STR("Ksmall") +HB_STR("Lsmall") +HB_STR("Msmall") +HB_STR("Nsmall") +HB_STR("Osmall") +HB_STR("Psmall") +HB_STR("Qsmall") +HB_STR("Rsmall") +HB_STR("Ssmall") +HB_STR("Tsmall") +HB_STR("Usmall") +HB_STR("Vsmall") +HB_STR("Wsmall") +HB_STR("Xsmall") +HB_STR("Ysmall") +HB_STR("Zsmall") +HB_STR("colonmonetary") +HB_STR("onefitted") +HB_STR("rupiah") +HB_STR("Tildesmall") +HB_STR("exclamdownsmall") +HB_STR("centoldstyle") +HB_STR("Lslashsmall") +HB_STR("Scaronsmall") +HB_STR("Zcaronsmall") +HB_STR("Dieresissmall") +HB_STR("Brevesmall") +HB_STR("Caronsmall") +HB_STR("Dotaccentsmall") +HB_STR("Macronsmall") +HB_STR("figuredash") +HB_STR("hypheninferior") +HB_STR("Ogoneksmall") +HB_STR("Ringsmall") +HB_STR("Cedillasmall") +HB_STR("questiondownsmall") +HB_STR("oneeighth") +HB_STR("threeeighths") +HB_STR("fiveeighths") +HB_STR("seveneighths") +HB_STR("onethird") +HB_STR("twothirds") +HB_STR("zerosuperior") +HB_STR("foursuperior") +HB_STR("fivesuperior") +HB_STR("sixsuperior") +HB_STR("sevensuperior") +HB_STR("eightsuperior") +HB_STR("ninesuperior") +HB_STR("zeroinferior") +HB_STR("oneinferior") +HB_STR("twoinferior") +HB_STR("threeinferior") +HB_STR("fourinferior") +HB_STR("fiveinferior") +HB_STR("sixinferior") +HB_STR("seveninferior") +HB_STR("eightinferior") +HB_STR("nineinferior") +HB_STR("centinferior") +HB_STR("dollarinferior") +HB_STR("periodinferior") +HB_STR("commainferior") +HB_STR("Agravesmall") +HB_STR("Aacutesmall") +HB_STR("Acircumflexsmall") +HB_STR("Atildesmall") +HB_STR("Adieresissmall") +HB_STR("Aringsmall") +HB_STR("AEsmall") +HB_STR("Ccedillasmall") +HB_STR("Egravesmall") +HB_STR("Eacutesmall") +HB_STR("Ecircumflexsmall") +HB_STR("Edieresissmall") +HB_STR("Igravesmall") +HB_STR("Iacutesmall") +HB_STR("Icircumflexsmall") +HB_STR("Idieresissmall") +HB_STR("Ethsmall") +HB_STR("Ntildesmall") +HB_STR("Ogravesmall") +HB_STR("Oacutesmall") +HB_STR("Ocircumflexsmall") +HB_STR("Otildesmall") +HB_STR("Odieresissmall") +HB_STR("OEsmall") +HB_STR("Oslashsmall") +HB_STR("Ugravesmall") +HB_STR("Uacutesmall") +HB_STR("Ucircumflexsmall") +HB_STR("Udieresissmall") +HB_STR("Yacutesmall") +HB_STR("Thornsmall") +HB_STR("Ydieresissmall") +HB_STR("001.000") +HB_STR("001.001") +HB_STR("001.002") +HB_STR("001.003") +HB_STR("Black") +HB_STR("Bold") +HB_STR("Book") +HB_STR("Light") +HB_STR("Medium") +HB_STR("Regular") +HB_STR("Roman") +HB_STR("Semibold") #endif /* HB_OT_CFF1_STD_STR_HH */ 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 27c4d31b9ce..778167eeb23 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 @@ -326,7 +326,7 @@ struct Charset0 void collect_glyph_to_sid_map (glyph_to_sid_map_t *mapping, unsigned int num_glyphs) const { - mapping->resize (num_glyphs, false); + mapping->resize_dirty (num_glyphs); for (hb_codepoint_t gid = 1; gid < num_glyphs; gid++) mapping->arrayZ[gid] = {sids[gid - 1], gid}; } @@ -426,7 +426,7 @@ struct Charset1_2 { void collect_glyph_to_sid_map (glyph_to_sid_map_t *mapping, unsigned int num_glyphs) const { - mapping->resize (num_glyphs, false); + mapping->resize_dirty (num_glyphs); hb_codepoint_t gid = 1; if (gid >= num_glyphs) return; 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 b2702106ecc..499bd942cde 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 @@ -202,7 +202,11 @@ struct cff2_cs_opset_path_t : cff2_cs_opset_tcoords, font->num_coords)); + return get_path_at (font, + glyph, + draw_session, + hb_array (font->coords, + font->has_nonzero_coords ? font->num_coords : 0)); } bool OT::cff2::accelerator_t::get_path_at (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session, hb_array_t coords) 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 6bf37c062fc..93e9c0a2697 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 @@ -501,10 +501,6 @@ struct CmapSubtableFormat4 this->length = c->length () - table_initpos; if ((long long) this->length != (long long) c->length () - table_initpos) { - // Length overflowed. Discard the current object before setting the error condition, otherwise - // discard is a noop which prevents the higher level code from reverting the serializer to the - // pre-error state in cmap4 overflow handling code. - c->pop_discard (); c->err (HB_SERIALIZE_ERROR_INT_OVERFLOW); return; } @@ -701,16 +697,7 @@ struct CmapSubtableFormat4 hb_barrier (); if (unlikely (!c->check_range (this, length))) - { - /* Some broken fonts have too long of a "length" value. - * If that is the case, just change the value to truncate - * the subtable at the end of the blob. */ - uint16_t new_length = (uint16_t) hb_min ((uintptr_t) 65535, - (uintptr_t) (c->end - - (char *) this)); - if (!c->try_set (&length, new_length)) - return_trace (false); - } + return_trace (false); return_trace (16 + 4 * (unsigned int) segCountX2 <= length); } @@ -1500,7 +1487,7 @@ struct CmapSubtable bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0 .get_glyph (codepoint, glyph); case 4: hb_barrier (); return u.format4 .get_glyph (codepoint, glyph); case 6: hb_barrier (); return u.format6 .get_glyph (codepoint, glyph); @@ -1513,7 +1500,7 @@ struct CmapSubtable } void collect_unicodes (hb_set_t *out, unsigned int num_glyphs = UINT_MAX) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); u.format0 .collect_unicodes (out); return; case 4: hb_barrier (); u.format4 .collect_unicodes (out); return; case 6: hb_barrier (); u.format6 .collect_unicodes (out); return; @@ -1529,7 +1516,7 @@ struct CmapSubtable hb_map_t *mapping, /* OUT */ unsigned num_glyphs = UINT_MAX) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); u.format0 .collect_mapping (unicodes, mapping); return; case 4: hb_barrier (); u.format4 .collect_mapping (unicodes, mapping); return; case 6: hb_barrier (); u.format6 .collect_mapping (unicodes, mapping); return; @@ -1543,7 +1530,7 @@ struct CmapSubtable unsigned get_language () const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0 .get_language (); case 4: hb_barrier (); return u.format4 .get_language (); case 6: hb_barrier (); return u.format6 .get_language (); @@ -1574,9 +1561,9 @@ struct CmapSubtable bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return_trace (u.format0 .sanitize (c)); case 4: hb_barrier (); return_trace (u.format4 .sanitize (c)); case 6: hb_barrier (); return_trace (u.format6 .sanitize (c)); @@ -1590,7 +1577,7 @@ struct CmapSubtable public: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ CmapSubtableFormat0 format0; CmapSubtableFormat4 format4; CmapSubtableFormat6 format6; @@ -1600,7 +1587,7 @@ struct CmapSubtable CmapSubtableFormat14 format14; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; @@ -1646,7 +1633,7 @@ struct EncodingRecord CmapSubtable *cmapsubtable = c->push (); unsigned origin_length = c->length (); cmapsubtable->serialize (c, it, format, plan, &(base+subtable)); - if (c->length () - origin_length > 0) *objidx = c->pop_pack (); + if (c->length () - origin_length > 0 && !c->in_error()) *objidx = c->pop_pack (); else c->pop_discard (); } @@ -1683,6 +1670,10 @@ struct SubtableUnicodesCache { { SubtableUnicodesCache* cache = (SubtableUnicodesCache*) hb_malloc (sizeof(SubtableUnicodesCache)); + + if (unlikely (!cache)) + return nullptr; + new (cache) SubtableUnicodesCache (source_table); return cache; } @@ -1776,6 +1767,10 @@ struct cmap ; SubtableUnicodesCache* cache = SubtableUnicodesCache::create(source_table); + + if (unlikely (!cache)) + return nullptr; + for (const EncodingRecord& _ : it) cache->set_for(&_); // populate the cache for this encoding record. @@ -1810,7 +1805,7 @@ struct cmap if (c->in_error ()) return false; - unsigned format = (base+_.subtable).u.format; + unsigned format = (base+_.subtable).u.format.v; if (format != 4 && format != 12 && format != 14) continue; const hb_set_t* unicodes_set = unicodes_cache->set_for (&_, local_unicodes_cache); @@ -1912,7 +1907,7 @@ struct cmap + hb_iter (encodingRecord) | hb_map (&EncodingRecord::subtable) | hb_map (hb_add (this)) - | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == 14; }) + | hb_filter ([&] (const CmapSubtable& _) { return _.u.format.v == 14; }) | hb_apply ([=] (const CmapSubtable& _) { _.u.format14.closure_glyphs (unicodes, glyphset); }) ; } @@ -1937,7 +1932,7 @@ struct cmap for (const EncodingRecord& _ : encodingrec_iter) { - unsigned format = (this + _.subtable).u.format; + unsigned format = (this + _.subtable).u.format.v; if (format == 12) has_format12 = true; const EncodingRecord *table = std::addressof (_); @@ -2025,7 +2020,7 @@ struct cmap this->subtable_uvs = &Null (CmapSubtableFormat14); { const CmapSubtable *st = table->find_subtable (0, 5); - if (st && st->u.format == 14) + if (st && st->u.format.v == 14) subtable_uvs = &st->u.format14; } @@ -2069,7 +2064,7 @@ struct cmap else #endif { - switch (subtable->u.format) { + switch (subtable->u.format.v) { /* Accelerate format 4 and format 12. */ default: this->get_glyph_funcZ = get_glyph_from; @@ -2276,7 +2271,7 @@ struct cmap (_.platformID == 0 && _.encodingID == 4) || (_.platformID == 3 && _.encodingID == 1) || (_.platformID == 3 && _.encodingID == 10) || - (cmap + _.subtable).u.format == 14; + (cmap + _.subtable).u.format.v == 14; } protected: 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 0238bb346a9..8add9209f37 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc @@ -37,6 +37,7 @@ #include "hb-ot-cmap-table.hh" #include "hb-ot-glyf-table.hh" +#include "hb-ot-var-gvar-table.hh" #include "hb-ot-cff2-table.hh" #include "hb-ot-cff1-table.hh" #include "hb-ot-hmtx-table.hh" @@ -64,18 +65,22 @@ using hb_ot_font_advance_cache_t = hb_cache_t<24, 16>; static_assert (sizeof (hb_ot_font_advance_cache_t) == 1024, ""); +using hb_ot_font_origin_cache_t = hb_cache_t<20, 20>; +static_assert (sizeof (hb_ot_font_origin_cache_t) == 1024, ""); + struct hb_ot_font_t { const hb_ot_face_t *ot_face; - /* h_advance caching */ + mutable hb_atomic_t cached_serial; mutable hb_atomic_t cached_coords_serial; - struct advance_cache_t + + struct direction_cache_t { mutable hb_atomic_t advance_cache; - mutable hb_atomic_t varStore_cache; + mutable hb_atomic_t varStore_cache; - ~advance_cache_t () + ~direction_cache_t () { clear (); } @@ -116,7 +121,7 @@ struct hb_ot_font_t goto retry; } - OT::ItemVariationStore::cache_t *acquire_varStore_cache (const OT::ItemVariationStore &varStore) const + OT::hb_scalar_cache_t *acquire_varStore_cache (const OT::ItemVariationStore &varStore) const { retry: auto *cache = varStore_cache.get_acquire (); @@ -127,7 +132,7 @@ struct hb_ot_font_t else goto retry; } - void release_varStore_cache (OT::ItemVariationStore::cache_t *cache) const + void release_varStore_cache (OT::hb_scalar_cache_t *cache) const { if (!cache) return; @@ -154,17 +159,157 @@ struct hb_ot_font_t } h, v; + struct origin_cache_t + { + mutable hb_atomic_t origin_cache; + mutable hb_atomic_t varStore_cache; + + ~origin_cache_t () + { + clear (); + } + + hb_ot_font_origin_cache_t *acquire_origin_cache () const + { + retry: + auto *cache = origin_cache.get_acquire (); + if (!cache) + { + cache = (hb_ot_font_origin_cache_t *) hb_malloc (sizeof (hb_ot_font_origin_cache_t)); + if (!cache) + return nullptr; + new (cache) hb_ot_font_origin_cache_t; + return cache; + } + if (origin_cache.cmpexch (cache, nullptr)) + return cache; + else + goto retry; + } + void release_origin_cache (hb_ot_font_origin_cache_t *cache) const + { + if (!cache) + return; + if (!origin_cache.cmpexch (nullptr, cache)) + hb_free (cache); + } + void clear_origin_cache () const + { + retry: + auto *cache = origin_cache.get_acquire (); + if (!cache) + return; + if (origin_cache.cmpexch (cache, nullptr)) + hb_free (cache); + else + goto retry; + } + + OT::hb_scalar_cache_t *acquire_varStore_cache (const OT::ItemVariationStore &varStore) const + { + retry: + auto *cache = varStore_cache.get_acquire (); + if (!cache) + return varStore.create_cache (); + if (varStore_cache.cmpexch (cache, nullptr)) + return cache; + else + goto retry; + } + void release_varStore_cache (OT::hb_scalar_cache_t *cache) const + { + if (!cache) + return; + if (!varStore_cache.cmpexch (nullptr, cache)) + OT::ItemVariationStore::destroy_cache (cache); + } + void clear_varStore_cache () const + { + retry: + auto *cache = varStore_cache.get_acquire (); + if (!cache) + return; + if (varStore_cache.cmpexch (cache, nullptr)) + OT::ItemVariationStore::destroy_cache (cache); + else + goto retry; + } + + void clear () const + { + clear_origin_cache (); + clear_varStore_cache (); + } + } v_origin; + + struct draw_cache_t + { + mutable hb_atomic_t gvar_cache; + + ~draw_cache_t () + { + clear (); + } + + OT::hb_scalar_cache_t *acquire_gvar_cache (const OT::gvar_accelerator_t &gvar) const + { + retry: + auto *cache = gvar_cache.get_acquire (); + if (!cache) + return gvar.create_cache (); + if (gvar_cache.cmpexch (cache, nullptr)) + return cache; + else + goto retry; + } + void release_gvar_cache (OT::hb_scalar_cache_t *cache) const + { + if (!cache) + return; + if (!gvar_cache.cmpexch (nullptr, cache)) + OT::gvar_accelerator_t::destroy_cache (cache); + } + void clear_gvar_cache () const + { + retry: + auto *cache = gvar_cache.get_acquire (); + if (!cache) + return; + if (gvar_cache.cmpexch (cache, nullptr)) + OT::gvar_accelerator_t::destroy_cache (cache); + else + goto retry; + } + + void clear () const + { + clear_gvar_cache (); + } + } draw; + void check_serial (hb_font_t *font) const { int font_serial = font->serial_coords.get_acquire (); + if (cached_serial.get_acquire () != font_serial) + { + /* These caches are dependent on scale and synthetic settings. + * Any change to the font invalidates them. */ + v_origin.clear (); - if (cached_coords_serial.get_acquire () == font_serial) - return; + cached_serial.set_release (font_serial); + } - h.clear (); - v.clear (); + int font_serial_coords = font->serial_coords.get_acquire (); + if (cached_coords_serial.get_acquire () != font_serial_coords) + { + /* These caches are independent of scale or synthetic settings. + * Just variation changes will invalidate them. */ + h.clear (); + v.clear (); + draw.clear (); - cached_coords_serial.set_release (font_serial); + cached_coords_serial.set_release (font_serial_coords); + } } }; @@ -242,37 +387,59 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, unsigned advance_stride, void *user_data HB_UNUSED) { + // Duplicated in v_advances. Ugly. Keep in sync'ish. 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; - ot_font->check_serial (font); - const OT::HVAR &HVAR = *hmtx.var_table; - const OT::ItemVariationStore &varStore = &HVAR + HVAR.varStore; - OT::ItemVariationStore::cache_t *varStore_cache = ot_font->h.acquire_varStore_cache (varStore); - - hb_ot_font_advance_cache_t *advance_cache = nullptr; - - bool use_cache = font->num_coords; - if (use_cache) - { - advance_cache = ot_font->h.acquire_advance_cache (); - if (!advance_cache) - use_cache = false; - } - - if (!use_cache) + if (unlikely (!hmtx.has_data ())) { + hb_position_t advance = font->face->get_upem () / 2; + advance = font->em_scale_x (advance); for (unsigned int i = 0; i < count; i++) { - *first_advance = font->em_scale_x (hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache)); + *first_advance = advance; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + return; + } + +#ifndef HB_NO_VAR + if (!font->has_nonzero_coords) + { + fallback: +#else + { +#endif + // Just plain htmx data. No need to cache. + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->em_scale_x (hmtx.get_advance_without_var_unscaled (*first_glyph)); first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } + return; } - else - { /* Use cache. */ + +#ifndef HB_NO_VAR + /* has_nonzero_coords. */ + + ot_font->check_serial (font); + hb_ot_font_advance_cache_t *advance_cache = ot_font->h.acquire_advance_cache (); + if (!advance_cache) + { + // malloc failure. Just use the fallback non-variable path. + goto fallback; + } + + /* If HVAR is present, use it.*/ + const OT::HVAR &HVAR = *hmtx.var_table; + if (HVAR.has_data ()) + { + const OT::ItemVariationStore &varStore = &HVAR + HVAR.varStore; + OT::hb_scalar_cache_t *varStore_cache = ot_font->h.acquire_varStore_cache (varStore); + for (unsigned int i = 0; i < count; i++) { hb_position_t v; @@ -289,10 +456,49 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } + ot_font->h.release_varStore_cache (varStore_cache); ot_font->h.release_advance_cache (advance_cache); + return; } - ot_font->h.release_varStore_cache (varStore_cache); + const auto &gvar = *ot_face->gvar; + if (gvar.has_data ()) + { + const auto &glyf = *ot_face->glyf; + auto *scratch = glyf.acquire_scratch (); + if (unlikely (!scratch)) + { + ot_font->h.release_advance_cache (advance_cache); + goto fallback; + } + OT::hb_scalar_cache_t *gvar_cache = ot_font->draw.acquire_gvar_cache (gvar); + + for (unsigned int i = 0; i < count; i++) + { + hb_position_t v; + unsigned cv; + if (advance_cache->get (*first_glyph, &cv)) + v = cv; + else + { + v = glyf.get_advance_with_var_unscaled (*first_glyph, font, false, *scratch, gvar_cache); + advance_cache->set (*first_glyph, v); + } + *first_advance = font->em_scale_x (v); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + + ot_font->draw.release_gvar_cache (gvar_cache); + glyf.release_scratch (scratch); + ot_font->h.release_advance_cache (advance_cache); + return; + } + + ot_font->h.release_advance_cache (advance_cache); + // No HVAR or GVAR. Just use the fallback non-variable path. + goto fallback; +#endif } #ifndef HB_NO_VERTICAL @@ -305,99 +511,290 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, unsigned advance_stride, void *user_data HB_UNUSED) { + // Duplicated from h_advances. Ugly. Keep in sync'ish. + 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::vmtx_accelerator_t &vmtx = *ot_face->vmtx; - if (vmtx.has_data ()) + if (unlikely (!vmtx.has_data ())) + { + hb_font_extents_t font_extents; + font->get_h_extents_with_fallback (&font_extents); + hb_position_t advance = font_extents.descender - font_extents.ascender; + for (unsigned int i = 0; i < count; i++) + { + *first_advance = advance; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + return; + } + +#ifndef HB_NO_VAR + if (!font->has_nonzero_coords) + { + fallback: +#else + { +#endif + // Just plain vtmx data. No need to cache. + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->em_scale_y (- (int) vmtx.get_advance_without_var_unscaled (*first_glyph)); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + return; + } + +#ifndef HB_NO_VAR + /* has_nonzero_coords. */ + + ot_font->check_serial (font); + hb_ot_font_advance_cache_t *advance_cache = ot_font->v.acquire_advance_cache (); + if (!advance_cache) + { + // malloc failure. Just use the fallback non-variable path. + goto fallback; + } + + /* If VVAR is present, use it.*/ + const OT::VVAR &VVAR = *vmtx.var_table; + if (VVAR.has_data ()) { - ot_font->check_serial (font); - const OT::VVAR &VVAR = *vmtx.var_table; const OT::ItemVariationStore &varStore = &VVAR + VVAR.varStore; - OT::ItemVariationStore::cache_t *varStore_cache = ot_font->v.acquire_varStore_cache (varStore); - // TODO Use advance_cache. + OT::hb_scalar_cache_t *varStore_cache = ot_font->v.acquire_varStore_cache (varStore); for (unsigned int i = 0; i < count; i++) { - *first_advance = font->em_scale_y (-(int) vmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache)); + hb_position_t v; + unsigned cv; + if (advance_cache->get (*first_glyph, &cv)) + v = cv; + else + { + v = vmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache); + advance_cache->set (*first_glyph, v); + } + *first_advance = font->em_scale_y (- (int) v); first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } ot_font->v.release_varStore_cache (varStore_cache); + ot_font->v.release_advance_cache (advance_cache); + return; } - else + + const auto &gvar = *ot_face->gvar; + if (gvar.has_data ()) { - hb_font_extents_t font_extents; - font->get_h_extents_with_fallback (&font_extents); - hb_position_t advance = -(font_extents.ascender - font_extents.descender); + const auto &glyf = *ot_face->glyf; + auto *scratch = glyf.acquire_scratch (); + if (unlikely (!scratch)) + { + ot_font->v.release_advance_cache (advance_cache); + goto fallback; + } + OT::hb_scalar_cache_t *gvar_cache = ot_font->draw.acquire_gvar_cache (gvar); for (unsigned int i = 0; i < count; i++) { - *first_advance = advance; + hb_position_t v; + unsigned cv; + if (advance_cache->get (*first_glyph, &cv)) + v = cv; + else + { + v = glyf.get_advance_with_var_unscaled (*first_glyph, font, true, *scratch, gvar_cache); + advance_cache->set (*first_glyph, v); + } + *first_advance = font->em_scale_y (- (int) v); first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } + + ot_font->draw.release_gvar_cache (gvar_cache); + glyf.release_scratch (scratch); + ot_font->v.release_advance_cache (advance_cache); + return; } + + ot_font->v.release_advance_cache (advance_cache); + // No VVAR or GVAR. Just use the fallback non-variable path. + goto fallback; +#endif } #endif #ifndef HB_NO_VERTICAL +HB_HOT static hb_bool_t -hb_ot_get_glyph_v_origin (hb_font_t *font, - void *font_data, - hb_codepoint_t glyph, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) +hb_ot_get_glyph_v_origins (hb_font_t *font, + void *font_data, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_x, + unsigned x_stride, + hb_position_t *first_y, + unsigned y_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; - *x = font->get_glyph_h_advance (glyph) / 2; - - const OT::VORG &VORG = *ot_face->VORG; - if (VORG.has_data ()) + /* First, set all the x values to half the advance width. */ + font->get_glyph_h_advances (count, + first_glyph, glyph_stride, + first_x, x_stride); + for (unsigned i = 0; i < count; i++) { - float delta = 0; + *first_x /= 2; + first_x = &StructAtOffsetUnaligned (first_x, x_stride); + } + /* The vertical origin business is messy... + * + * We allocate the cache, then have various code paths that use the cache. + * Each one is responsible to free it before returning. + */ + hb_ot_font_origin_cache_t *origin_cache = ot_font->v_origin.acquire_origin_cache (); + + /* If there is VORG, always use it. It uses VVAR for variations if necessary. */ + const OT::VORG &VORG = *ot_face->VORG; + if (origin_cache && VORG.has_data ()) + { #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); + if (!font->has_nonzero_coords) #endif + { + for (unsigned i = 0; i < count; i++) + { + hb_position_t origin; + unsigned cv; + if (origin_cache->get (*first_glyph, &cv)) + origin = font->y_scale < 0 ? -static_cast(cv) : static_cast(cv); + else + { + origin = font->em_scalef_y (VORG.get_y_origin (*first_glyph)); + origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin); + } - *y = font->em_scalef_y (VORG.get_y_origin (glyph) + delta); + *first_y = origin; + + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + } +#ifndef HB_NO_VAR + else + { + const OT::VVAR &VVAR = *ot_face->vmtx->var_table; + const auto &varStore = &VVAR + VVAR.varStore; + auto *varStore_cache = ot_font->v_origin.acquire_varStore_cache (varStore); + for (unsigned i = 0; i < count; i++) + { + hb_position_t origin; + unsigned cv; + if (origin_cache->get (*first_glyph, &cv)) + origin = font->y_scale < 0 ? -static_cast(cv) : static_cast(cv); + else + { + origin = font->em_scalef_y (VORG.get_y_origin (*first_glyph) + + VVAR.get_vorg_delta_unscaled (*first_glyph, + font->coords, font->num_coords, + varStore_cache)); + origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin); + } + + *first_y = origin; + + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + ot_font->v_origin.release_varStore_cache (varStore_cache); + } +#endif + ot_font->v_origin.release_origin_cache (origin_cache); return true; } - hb_glyph_extents_t extents = {0}; - - if (hb_font_get_glyph_extents (font, glyph, &extents)) + /* If and only if `vmtx` is present and it's a `glyf` font, + * we use the top phantom point, deduced from vmtx,glyf[,gvar]. */ + const auto &vmtx = *ot_face->vmtx; + const auto &glyf = *ot_face->glyf; + if (origin_cache && vmtx.has_data() && glyf.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)) + auto *scratch = glyf.acquire_scratch (); + if (unlikely (!scratch)) { - *y = extents.y_bearing + font->em_scale_y (tsb); - return true; + ot_font->v_origin.release_origin_cache (origin_cache); + return false; + } + OT::hb_scalar_cache_t *gvar_cache = font->has_nonzero_coords ? + ot_font->draw.acquire_gvar_cache (*ot_face->gvar) : + nullptr; + + for (unsigned i = 0; i < count; i++) + { + hb_position_t origin; + unsigned cv; + if (origin_cache->get (*first_glyph, &cv)) + origin = font->y_scale < 0 ? -static_cast(cv) : static_cast(cv); + else + { + origin = font->em_scalef_y (glyf.get_v_origin_with_var_unscaled (*first_glyph, font, *scratch, gvar_cache)); + origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin); + } + + *first_y = origin; + + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); } - hb_font_extents_t font_extents; - font->get_h_extents_with_fallback (&font_extents); - hb_position_t advance = font_extents.ascender - font_extents.descender; - hb_position_t diff = advance - -extents.height; - *y = extents.y_bearing + (diff >> 1); + if (gvar_cache) + ot_font->draw.release_gvar_cache (gvar_cache); + glyf.release_scratch (scratch); + ot_font->v_origin.release_origin_cache (origin_cache); return true; } - hb_font_extents_t font_extents; - font->get_h_extents_with_fallback (&font_extents); - *y = font_extents.ascender; + /* Otherwise, use glyph extents to center the glyph vertically. + * If getting glyph extents failed, just use the font ascender. */ + if (origin_cache && font->has_glyph_extents_func ()) + { + hb_font_extents_t font_extents; + font->get_h_extents_with_fallback (&font_extents); + hb_position_t font_advance = font_extents.ascender - font_extents.descender; + for (unsigned i = 0; i < count; i++) + { + hb_position_t origin; + unsigned cv; + + if (origin_cache->get (*first_glyph, &cv)) + origin = font->y_scale < 0 ? -static_cast(cv) : static_cast(cv); + else + { + hb_glyph_extents_t extents = {0}; + if (likely (font->get_glyph_extents (*first_glyph, &extents))) + origin = extents.y_bearing + ((font_advance - -extents.height) >> 1); + else + origin = font_extents.ascender; + + origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin); + } + + *first_y = origin; + + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + } + + ot_font->v_origin.release_origin_cache (origin_cache); return true; } #endif @@ -498,17 +895,33 @@ hb_ot_draw_glyph_or_fail (hb_font_t *font, hb_draw_funcs_t *draw_funcs, void *draw_data, void *user_data) { + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; hb_draw_session_t draw_session {draw_funcs, draw_data}; + bool ret = false; + + OT::hb_scalar_cache_t *gvar_cache = nullptr; + if (font->num_coords) + { + ot_font->check_serial (font); + gvar_cache = ot_font->draw.acquire_gvar_cache (*ot_font->ot_face->gvar); + } + #ifndef HB_NO_VAR_COMPOSITES - if (font->face->table.VARC->get_path (font, glyph, draw_session)) return true; + if (font->face->table.VARC->get_path (font, glyph, draw_session)) { ret = true; goto done; } #endif // Keep the following in synch with VARC::get_path_at() - if (font->face->table.glyf->get_path (font, glyph, draw_session)) return true; + if (font->face->table.glyf->get_path (font, glyph, draw_session, gvar_cache)) { ret = true; goto done; } + #ifndef HB_NO_CFF - if (font->face->table.cff2->get_path (font, glyph, draw_session)) return true; - if (font->face->table.cff1->get_path (font, glyph, draw_session)) return true; + if (font->face->table.cff2->get_path (font, glyph, draw_session)) { ret = true; goto done; } + if (font->face->table.cff1->get_path (font, glyph, draw_session)) { ret = true; goto done; } #endif - return false; + +done: + + ot_font->draw.release_gvar_cache (gvar_cache); + + return ret; } #endif @@ -548,12 +961,11 @@ static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_tplan->new_to_old_gid_list) - | hb_map ([c, &_mtx, mtx_map] (hb_codepoint_pair_t _) + | hb_map ([&_mtx, mtx_map] (hb_codepoint_pair_t _) { hb_codepoint_t new_gid = _.first; hb_codepoint_t old_gid = _.second; @@ -246,8 +236,7 @@ struct hmtxvmtx if (!mtx_map->has (new_gid, &v)) { int lsb = 0; - if (!_mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb)) - (void) _glyf_get_leading_bearing_without_var_unscaled (c->plan->source, old_gid, !T::is_horizontal, &lsb); + _mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb); return hb_pair (_mtx.get_advance_without_var_unscaled (old_gid), +lsb); } return *v; @@ -326,49 +315,23 @@ struct hmtxvmtx bool has_data () const { return (bool) num_bearings; } - bool get_leading_bearing_without_var_unscaled (hb_codepoint_t glyph, + void get_leading_bearing_without_var_unscaled (hb_codepoint_t glyph, int *lsb) const { if (glyph < num_long_metrics) { *lsb = table->longMetricZ[glyph].sb; - return true; + return; } if (unlikely (glyph >= num_bearings)) - return false; + { + *lsb = 0; + return; + } const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics]; *lsb = bearings[glyph - num_long_metrics]; - return true; - } - - bool get_leading_bearing_with_var_unscaled (hb_font_t *font, - hb_codepoint_t glyph, - int *lsb) const - { - if (!font->num_coords) - return get_leading_bearing_without_var_unscaled (glyph, lsb); - -#ifndef HB_NO_VAR - 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; - } - - // If there's no vmtx data, the phantom points from glyf table are not accurate, - // so we cannot take the next path. - bool is_vertical = T::tableTag == HB_OT_TAG_vmtx; - if (is_vertical && !has_data ()) - return false; - - return _glyf_get_leading_bearing_with_var_unscaled (font, glyph, is_vertical, lsb); -#else - return false; -#endif } unsigned int get_advance_without_var_unscaled (hb_codepoint_t glyph) const @@ -402,27 +365,17 @@ struct hmtxvmtx return advances[hb_min (glyph - num_bearings, num_advances - num_bearings - 1)]; } - unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph, - hb_font_t *font, - ItemVariationStore::cache_t *store_cache = nullptr) const +#ifndef HB_NO_VAR + unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph, + hb_font_t *font, + hb_scalar_cache_t *store_cache = nullptr) const { 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_delta_unscaled (glyph, - font->coords, font->num_coords, - store_cache)); - - unsigned glyf_advance = _glyf_get_advance_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx); - return glyf_advance ? glyf_advance : advance; -#else - return advance; -#endif + return hb_max(0.0f, advance + roundf (var_table->get_advance_delta_unscaled (glyph, + font->coords, font->num_coords, + store_cache))); } +#endif protected: // 0 <= num_long_metrics <= num_bearings <= num_advances <= num_glyphs diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh index 97d012e58b8..42326adcad2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh @@ -90,11 +90,11 @@ struct KernSubTableFormat3 template void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const { - set_t set; if (likely (glyphCount)) - set.add_range (0, glyphCount - 1); - left_set.union_ (set); - right_set.union_ (set); + { + left_set.add_range (0, num_glyphs - 1); + right_set.add_range (0, num_glyphs - 1); + } } protected: @@ -306,8 +306,8 @@ struct kern { static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; - bool has_data () const { return u.version32; } - unsigned get_type () const { return u.major; } + bool has_data () const { return u.version32.v; } + unsigned get_type () const { return u.major.v; } bool has_state_machine () const { @@ -363,7 +363,7 @@ struct kern bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.version32.sanitize (c)) return_trace (false); + if (!u.version32.v.sanitize (c)) return_trace (false); hb_barrier (); return_trace (dispatch (c)); } @@ -406,15 +406,15 @@ struct kern protected: union { - HBUINT32 version32; - HBUINT16 major; + struct { HBUINT32 v; } version32; + struct { HBUINT16 v; } major; KernOT ot; #ifndef HB_NO_AAT_SHAPE KernAAT aat; #endif } u; public: - DEFINE_SIZE_UNION (4, version32); + DEFINE_SIZE_UNION (4, version32.v); }; struct kern_accelerator_t : kern::accelerator_t { 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 04a79a93a89..b9baaca2a23 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 @@ -165,13 +165,13 @@ struct BaseCoordFormat3 struct BaseCoord { - bool has_data () const { return u.format; } + bool has_data () const { return u.format.v; } hb_position_t get_coord (hb_font_t *font, const ItemVariationStore &var_store, hb_direction_t direction) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_coord (font, direction); case 2: hb_barrier (); return u.format2.get_coord (font, direction); case 3: hb_barrier (); return u.format3.get_coord (font, var_store, direction); @@ -181,7 +181,7 @@ struct BaseCoord void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const { - switch (u.format) { + switch (u.format.v) { case 3: hb_barrier (); u.format3.collect_variation_indices (varidx_set); return; default:return; } @@ -190,9 +190,9 @@ struct BaseCoord 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) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward (ds)...)); case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward (ds)...)); @@ -203,9 +203,9 @@ struct BaseCoord bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (unlikely (!u.format.sanitize (c))) return_trace (false); + if (unlikely (!u.format.v.sanitize (c))) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); case 3: hb_barrier (); return_trace (u.format3.sanitize (c)); @@ -215,13 +215,13 @@ struct BaseCoord protected: union { - HBUINT16 format; + struct { HBUINT16 v; } format; BaseCoordFormat1 format1; BaseCoordFormat2 format2; BaseCoordFormat3 format3; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; struct FeatMinMaxRecord 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 7ee34185575..dcacc9cb86c 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 @@ -141,6 +141,7 @@ 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_map_t *feature_map_w_duplicates; const hb_hashmap_t *feature_substitutes_map; hb_hashmap_t> *feature_record_cond_idx_map; const hb_set_t *catch_all_record_feature_idxes; @@ -165,6 +166,7 @@ struct hb_subset_layout_context_t : lookup_index_map = &c_->plan->gsub_lookups; script_langsys_map = &c_->plan->gsub_langsys; feature_index_map = &c_->plan->gsub_features; + feature_map_w_duplicates = &c_->plan->gsub_features_w_duplicates; 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; catch_all_record_feature_idxes = &c_->plan->gsub_old_features; @@ -175,6 +177,7 @@ struct hb_subset_layout_context_t : lookup_index_map = &c_->plan->gpos_lookups; script_langsys_map = &c_->plan->gpos_langsys; feature_index_map = &c_->plan->gpos_features; + feature_map_w_duplicates = &c_->plan->gpos_features_w_duplicates; 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; catch_all_record_feature_idxes = &c_->plan->gpos_old_features; @@ -825,46 +828,9 @@ struct Feature const Record_sanitize_closure_t *closure = nullptr) const { TRACE_SANITIZE (this); - if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c)))) - return_trace (false); - hb_barrier (); - - /* Some earlier versions of Adobe tools calculated the offset of the - * FeatureParams subtable from the beginning of the FeatureList table! - * - * If sanitizing "failed" for the FeatureParams subtable, try it with the - * alternative location. We would know sanitize "failed" if old value - * of the offset was non-zero, but it's zeroed now. - * - * Only do this for the 'size' feature, since at the time of the faulty - * Adobe tools, only the 'size' feature had FeatureParams defined. - */ - - if (likely (featureParams.is_null ())) - return_trace (true); - - unsigned int orig_offset = featureParams; - if (unlikely (!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))) - return_trace (false); - hb_barrier (); - - if (featureParams == 0 && closure && - closure->tag == HB_TAG ('s','i','z','e') && - closure->list_base && closure->list_base < this) - { - unsigned int new_offset_int = orig_offset - - (((char *) this) - ((char *) closure->list_base)); - - Offset16To new_offset; - /* Check that it would not overflow. */ - new_offset = new_offset_int; - if (new_offset == new_offset_int && - c->try_set (&featureParams, new_offset_int) && - !featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE)) - return_trace (false); - } - - return_trace (true); + return_trace (c->check_struct (this) && + featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE) && + lookupIndex.sanitize (c)); } Offset16To @@ -1082,15 +1048,15 @@ struct LangSys if (unlikely (!c->serializer->extend_min (out))) return_trace (false); const uint32_t *v; - out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex, &v) ? *v : 0xFFFFu; + out->reqFeatureIndex = l->feature_map_w_duplicates->has (reqFeatureIndex, &v) ? *v : 0xFFFFu; if (!l->visitFeatureIndex (featureIndex.len)) return_trace (false); auto it = + hb_iter (featureIndex) - | hb_filter (l->feature_index_map) - | hb_map (l->feature_index_map) + | hb_filter (l->feature_map_w_duplicates) + | hb_map (l->feature_map_w_duplicates) ; bool ret = bool (it); @@ -1337,7 +1303,7 @@ struct Lookup TRACE_DISPATCH (this, lookup_type); unsigned int count = get_subtable_count (); for (unsigned int i = 0; i < count; i++) { - typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type, std::forward (ds)...); + typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type, ds...); if (c->stop_sublookup_iteration (r)) return_trace (r); } @@ -1387,6 +1353,11 @@ struct Lookup { unsigned new_flag = lookupFlag; new_flag &= ~LookupFlag::UseMarkFilteringSet; + // https://github.com/harfbuzz/harfbuzz/issues/5499 + // If we remove UseMarkFilteringSet flag because the set is now empty, + // we need to add IgnoreMarks flag, otherwise the lookup will not + // ignore any marks, which changes the behavior. + new_flag |= LookupFlag::IgnoreMarks; out->lookupFlag = new_flag; } else @@ -1425,7 +1396,7 @@ struct Lookup if (unlikely (!get_subtables ().sanitize (c, this, get_type ()))) return_trace (false); - if (unlikely (get_type () == TSubTable::Extension && !c->get_edit_count ())) + if (unlikely (get_type () == TSubTable::Extension)) { hb_barrier (); @@ -1433,11 +1404,6 @@ struct Lookup * have the same type, which shall not be the Extension type * itself (but we already checked for that). * This is specially important if one has a reverse type! - * - * We only do this if sanitizer edit_count is zero. Otherwise, - * some of the subtables might have become insane after they - * were sanity-checked by the edits of subsequent subtables. - * https://bugs.chromium.org/p/chromium/issues/detail?id=960331 */ unsigned int type = get_subtable (0).u.extension.get_type (); for (unsigned int i = 1; i < subtables; i++) @@ -2067,7 +2033,7 @@ struct ClassDef unsigned int get (hb_codepoint_t k) const { return get_class (k); } unsigned int get_class (hb_codepoint_t glyph_id) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_class (glyph_id); case 2: hb_barrier (); return u.format2.get_class (glyph_id); #ifndef HB_NO_BEYOND_64K @@ -2078,7 +2044,7 @@ struct ClassDef } } unsigned int get_class (hb_codepoint_t glyph_id, - hb_ot_lookup_cache_t *cache) const + hb_ot_layout_mapping_cache_t *cache) const { unsigned klass; if (cache && cache->get (glyph_id, &klass)) return klass; @@ -2089,7 +2055,7 @@ struct ClassDef unsigned get_population () const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_population (); case 2: hb_barrier (); return u.format2.get_population (); #ifndef HB_NO_BEYOND_64K @@ -2142,7 +2108,7 @@ struct ClassDef #ifndef HB_NO_BEYOND_64K if (glyph_max > 0xFFFFu) - u.format += 2; + u.format.v += 2; if (unlikely (glyph_max > 0xFFFFFFu)) #else if (unlikely (glyph_max > 0xFFFFu)) @@ -2152,9 +2118,9 @@ struct ClassDef return_trace (false); } - u.format = format; + u.format.v = format; - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.serialize (c, it)); case 2: hb_barrier (); return_trace (u.format2.serialize (c, it)); @@ -2173,7 +2139,7 @@ struct ClassDef const Coverage* glyph_filter = nullptr) const { TRACE_SUBSET (this); - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); case 2: hb_barrier (); return_trace (u.format2.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); #ifndef HB_NO_BEYOND_64K @@ -2187,9 +2153,9 @@ struct ClassDef bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); #ifndef HB_NO_BEYOND_64K @@ -2202,7 +2168,7 @@ struct ClassDef unsigned cost () const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.cost (); case 2: hb_barrier (); return u.format2.cost (); #ifndef HB_NO_BEYOND_64K @@ -2218,7 +2184,7 @@ struct ClassDef template bool collect_coverage (set_t *glyphs) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.collect_coverage (glyphs); case 2: hb_barrier (); return u.format2.collect_coverage (glyphs); #ifndef HB_NO_BEYOND_64K @@ -2234,7 +2200,7 @@ struct ClassDef template bool collect_class (set_t *glyphs, unsigned int klass) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.collect_class (glyphs, klass); case 2: hb_barrier (); return u.format2.collect_class (glyphs, klass); #ifndef HB_NO_BEYOND_64K @@ -2247,7 +2213,7 @@ struct ClassDef bool intersects (const hb_set_t *glyphs) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.intersects (glyphs); case 2: hb_barrier (); return u.format2.intersects (glyphs); #ifndef HB_NO_BEYOND_64K @@ -2259,7 +2225,7 @@ struct ClassDef } bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.intersects_class (glyphs, klass); case 2: hb_barrier (); return u.format2.intersects_class (glyphs, klass); #ifndef HB_NO_BEYOND_64K @@ -2272,7 +2238,7 @@ struct ClassDef void intersected_class_glyphs (const hb_set_t *glyphs, unsigned klass, hb_set_t *intersect_glyphs) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.intersected_class_glyphs (glyphs, klass, intersect_glyphs); case 2: hb_barrier (); return u.format2.intersected_class_glyphs (glyphs, klass, intersect_glyphs); #ifndef HB_NO_BEYOND_64K @@ -2285,7 +2251,7 @@ struct ClassDef void intersected_classes (const hb_set_t *glyphs, hb_set_t *intersect_classes) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.intersected_classes (glyphs, intersect_classes); case 2: hb_barrier (); return u.format2.intersected_classes (glyphs, intersect_classes); #ifndef HB_NO_BEYOND_64K @@ -2299,7 +2265,7 @@ struct ClassDef protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ClassDefFormat1_3 format1; ClassDefFormat2_4 format2; #ifndef HB_NO_BEYOND_64K @@ -2308,7 +2274,7 @@ struct ClassDef #endif } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; template @@ -2326,151 +2292,177 @@ struct delta_row_encoding_t { /* each byte represents a region, value is one of 0/1/2/4, which means bytes * needed for this region */ - hb_vector_t chars; + struct chars_t : hb_vector_t + { + int cmp (const chars_t& other) const + { + return as_array ().cmp (other.as_array ()); + } + + hb_pair_t get_width () + { + unsigned width = 0; + unsigned columns = 0; + for (unsigned i = 0; i < length; i++) + { + unsigned v = arrayZ[i]; + width += v; + columns += (v != 0); + } + return hb_pair (width, columns); + } + + HB_HOT + hb_pair_t combine_width (const chars_t& other) const + { + unsigned combined_width = 0; + unsigned combined_columns = 0; + for (unsigned i = 0; i < length; i++) + { + unsigned v = hb_max (arrayZ[i], other.arrayZ[i]); + combined_width += v; + combined_columns += (v != 0); + } + return hb_pair (combined_width, combined_columns); + } + }; + + hb_pair_t combine_width (const delta_row_encoding_t& other_encoding) const { return chars.combine_width (other_encoding.chars); } + + // Actual data + + chars_t chars; unsigned width = 0; - hb_vector_t columns; unsigned overhead = 0; hb_vector_t*> items; delta_row_encoding_t () = default; - delta_row_encoding_t (hb_vector_t&& chars_, - const hb_vector_t* row = nullptr) : - delta_row_encoding_t () - + delta_row_encoding_t (hb_vector_t*> &&rows, unsigned num_cols) { - chars = std::move (chars_); - width = get_width (); - columns = get_columns (); - overhead = get_chars_overhead (columns); - if (row) items.push (row); + assert (rows); + + items = std::move (rows); + + if (unlikely (!chars.resize (num_cols))) + return; + + calculate_chars (); + } + + void merge (const delta_row_encoding_t& other) + { + items.alloc (items.length + other.items.length); + for (auto &row : other.items) + add_row (row); + + // Merge chars + assert (chars.length == other.chars.length); + for (unsigned i = 0; i < chars.length; i++) + chars.arrayZ[i] = hb_max (chars.arrayZ[i], other.chars.arrayZ[i]); + chars_changed (); + } + + void chars_changed () + { + auto _ = chars.get_width (); + width = _.first; + overhead = get_chars_overhead (_.second); + } + + void calculate_chars () + { + assert (items); + + bool long_words = false; + + for (auto &row : items) + { + assert (row->length == chars.length); + + /* 0/1/2 byte encoding */ + for (unsigned i = 0; i < row->length; i++) + { + int v = row->arrayZ[i]; + if (v == 0) + continue; + else if (v > 32767 || v < -32768) + { + long_words = true; + chars.arrayZ[i] = hb_max (chars.arrayZ[i], 4); + } + else if (v > 127 || v < -128) + chars.arrayZ[i] = hb_max (chars.arrayZ[i], 2); + else + chars.arrayZ[i] = hb_max (chars.arrayZ[i], 1); + } + } + + if (long_words) + { + // Convert 1s to 2s + for (auto &v : chars) + if (v == 1) + v = 2; + } + + chars_changed (); } bool is_empty () const { return !items; } - static hb_vector_t get_row_chars (const hb_vector_t& row) - { - hb_vector_t ret; - if (!ret.alloc (row.length)) return ret; - - bool long_words = false; - - /* 0/1/2 byte encoding */ - for (int i = row.length - 1; i >= 0; i--) - { - int v = row.arrayZ[i]; - if (v == 0) - ret.push (0); - else if (v > 32767 || v < -32768) - { - long_words = true; - break; - } - else if (v > 127 || v < -128) - ret.push (2); - else - ret.push (1); - } - - if (!long_words) - return ret; - - /* redo, 0/2/4 bytes encoding */ - ret.reset (); - for (int i = row.length - 1; i >= 0; i--) - { - int v = row.arrayZ[i]; - if (v == 0) - ret.push (0); - else if (v > 32767 || v < -32768) - ret.push (4); - else - ret.push (2); - } - return ret; - } - - inline unsigned get_width () - { - unsigned ret = + hb_iter (chars) - | hb_reduce (hb_add, 0u) - ; - return ret; - } - - hb_vector_t get_columns () - { - hb_vector_t cols; - cols.alloc (chars.length); - for (auto v : chars) - { - uint8_t flag = v ? 1 : 0; - cols.push (flag); - } - return cols; - } - - static inline unsigned get_chars_overhead (const hb_vector_t& cols) + static inline unsigned get_chars_overhead (unsigned num_columns) { unsigned c = 4 + 6; // 4 bytes for LOffset, 6 bytes for VarData header - unsigned cols_bit_count = 0; - for (auto v : cols) - if (v) cols_bit_count++; - return c + cols_bit_count * 2; + return c + num_columns * 2; } - unsigned get_gain () const + unsigned get_gain (unsigned additional_bytes_per_rows = 1) const { int count = items.length; - return hb_max (0, (int) overhead - count); + return hb_max (0, (int) overhead - count * (int) additional_bytes_per_rows); } int gain_from_merging (const delta_row_encoding_t& other_encoding) const { - int combined_width = 0; - for (unsigned i = 0; i < chars.length; i++) - combined_width += hb_max (chars.arrayZ[i], other_encoding.chars.arrayZ[i]); + // Back of the envelope calculations to reject early. + signed additional_bytes_per_rows = other_encoding.width - width; + if (additional_bytes_per_rows > 0) + { + if (get_gain (additional_bytes_per_rows) == 0) + return 0; + } + else + { + if (other_encoding.get_gain (-additional_bytes_per_rows) == 0) + return 0; + } - hb_vector_t combined_columns; - combined_columns.alloc (columns.length); - for (unsigned i = 0; i < columns.length; i++) - combined_columns.push (columns.arrayZ[i] | other_encoding.columns.arrayZ[i]); + auto pair = combine_width (other_encoding); + unsigned combined_width = pair.first; + unsigned combined_columns = pair.second; - int combined_overhead = get_chars_overhead (combined_columns); - int combined_gain = (int) overhead + (int) other_encoding.overhead - combined_overhead - - (combined_width - (int) width) * items.length - - (combined_width - (int) other_encoding.width) * other_encoding.items.length; + int combined_gain = (int) overhead + (int) other_encoding.overhead; + combined_gain -= (combined_width - (int) width) * items.length; + combined_gain -= (combined_width - (int) other_encoding.width) * other_encoding.items.length; + combined_gain -= get_chars_overhead (combined_columns); return combined_gain; } + bool add_row (const hb_vector_t* row) + { return items.push (row); } + static int cmp (const void *pa, const void *pb) { const delta_row_encoding_t *a = (const delta_row_encoding_t *)pa; const delta_row_encoding_t *b = (const delta_row_encoding_t *)pb; - int gain_a = a->get_gain (); - int gain_b = b->get_gain (); - - if (gain_a != gain_b) - return gain_a - gain_b; - - return (b->chars).as_array ().cmp ((a->chars).as_array ()); - } - - static int cmp_width (const void *pa, const void *pb) - { - const delta_row_encoding_t *a = (const delta_row_encoding_t *)pa; - const delta_row_encoding_t *b = (const delta_row_encoding_t *)pb; - if (a->width != b->width) return (int) a->width - (int) b->width; - return (b->chars).as_array ().cmp ((a->chars).as_array ()); + return b->chars.cmp (a->chars); } - - bool add_row (const hb_vector_t* row) - { return items.push (row); } }; struct VarRegionAxis @@ -2548,32 +2540,112 @@ struct SparseVarRegionAxis DEFINE_SIZE_STATIC (8); }; -#define REGION_CACHE_ITEM_CACHE_INVALID INT_MIN -#define REGION_CACHE_ITEM_MULTIPLIER (float (1 << ((sizeof (int) * 8) - 2))) -#define REGION_CACHE_ITEM_DIVISOR (1.f / float (1 << ((sizeof (int) * 8) - 2))) +struct hb_scalar_cache_t +{ + private: + static constexpr unsigned STATIC_LENGTH = 16; + static constexpr int INVALID = INT_MIN; + static constexpr float MULTIPLIER = 1 << ((sizeof (int) * 8) - 2); + static constexpr float DIVISOR = 1.f / MULTIPLIER; + + public: + hb_scalar_cache_t () : length (STATIC_LENGTH) { clear (); } + + hb_scalar_cache_t (const hb_scalar_cache_t&) = delete; + hb_scalar_cache_t (hb_scalar_cache_t&&) = delete; + hb_scalar_cache_t& operator= (const hb_scalar_cache_t&) = delete; + hb_scalar_cache_t& operator= (hb_scalar_cache_t&&) = delete; + + static hb_scalar_cache_t *create (unsigned int count, + hb_scalar_cache_t *scratch_cache = nullptr) + { + if (!count) return (hb_scalar_cache_t *) &Null(hb_scalar_cache_t); + + if (scratch_cache && count <= scratch_cache->length) + { + scratch_cache->clear (); + return scratch_cache; + } + + auto *cache = (hb_scalar_cache_t *) hb_malloc (sizeof (hb_scalar_cache_t) - sizeof (static_values) + sizeof (static_values[0]) * count); + if (unlikely (!cache)) return (hb_scalar_cache_t *) &Null(hb_scalar_cache_t); + + cache->length = count; + cache->clear (); + + return cache; + } + + static void destroy (hb_scalar_cache_t *cache, + hb_scalar_cache_t *scratch_cache = nullptr) + { + if (cache != &Null(hb_scalar_cache_t) && cache != scratch_cache) + hb_free (cache); + } + + void clear () + { + auto *values = &static_values[0]; + unsigned i = 0; +#ifndef HB_OPTIMIZE_SIZE + for (; i + 3 < length; i += 4) + { + values[i + 0] = INVALID; + values[i + 1] = INVALID; + values[i + 2] = INVALID; + values[i + 3] = INVALID; + } +#endif + for (; i < length; i++) + values[i] = INVALID; + } + + HB_ALWAYS_INLINE + bool get (unsigned i, float *value) const + { + if (unlikely (i >= length)) + { + *value = 0.f; + return true; + } + auto *values = &static_values[0]; + auto *cached_value = &values[i]; + // Super hot. Most common path is that we have a cached value of 0. + int v = *cached_value; + if (likely (!v)) + { + *value = 0.f; + return true; + } + if (v == INVALID) + return false; + *value = v * DIVISOR; + return true; + } + + HB_ALWAYS_INLINE + void set (unsigned i, float value) + { + if (unlikely (i >= length)) return; + auto *values = &static_values[0]; + auto *cached_value = &values[i]; + *cached_value = roundf(value * MULTIPLIER); + } + + private: + unsigned length; + mutable hb_atomic_t static_values[STATIC_LENGTH]; +}; struct VarRegionList { - using cache_t = hb_atomic_t; - - float evaluate (unsigned int region_index, - const int *coords, unsigned int coord_len, - cache_t *cache = nullptr) const + private: + float evaluate_impl (unsigned int region_index, + const int *coords, unsigned int coord_len) const { - if (unlikely (region_index >= regionCount)) - return 0.; - - cache_t *cached_value = nullptr; - if (cache) - { - cached_value = &(cache[region_index]); - if (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID) - return *cached_value * REGION_CACHE_ITEM_DIVISOR; - } - const VarRegionAxis *axes = axesZ.arrayZ + (region_index * axisCount); + float v = 1.f; - float v = 1.; unsigned int count = axisCount; for (unsigned int i = 0; i < count; i++) { @@ -2581,15 +2653,32 @@ struct VarRegionList float factor = axes[i].evaluate (coord); if (factor == 0.f) { - if (cache) - *cached_value = 0.; - return 0.; + v = 0.f; + break; } v *= factor; } + return v; + } + + public: + HB_ALWAYS_INLINE + float evaluate (unsigned int region_index, + const int *coords, unsigned int coord_len, + hb_scalar_cache_t *cache = nullptr) const + { + if (unlikely (region_index >= regionCount)) + return 0.; + + float v; + if (cache && cache->get (region_index, &v)) + return v; + + v = evaluate_impl (region_index, coords, coord_len); + if (cache) - *cached_value = v * REGION_CACHE_ITEM_MULTIPLIER; + cache->set (region_index, v); return v; } @@ -2732,29 +2821,24 @@ struct SparseVariationRegion : Array16Of struct SparseVarRegionList { - using cache_t = hb_atomic_t; - + HB_ALWAYS_INLINE float evaluate (unsigned int region_index, const int *coords, unsigned int coord_len, - cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { if (unlikely (region_index >= regions.len)) return 0.; - cache_t *cached_value = nullptr; - if (cache) - { - cached_value = &(cache[region_index]); - if (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID) - return *cached_value * REGION_CACHE_ITEM_DIVISOR; - } + float v; + if (cache && cache->get (region_index, &v)) + return v; const SparseVariationRegion ®ion = this+regions[region_index]; - float v = region.evaluate (coords, coord_len); - + v = region.evaluate (coords, coord_len); if (cache) - *cached_value = v * REGION_CACHE_ITEM_MULTIPLIER; + cache->set (region_index, v); + return v; } @@ -2792,46 +2876,62 @@ struct VarData + itemCount * get_row_size (); } - float get_delta (unsigned int inner, - const int *coords, unsigned int coord_count, - const VarRegionList ®ions, - VarRegionList::cache_t *cache = nullptr) const + float _get_delta (unsigned int inner, + const int *coords, unsigned int coord_count, + const VarRegionList ®ions, + hb_scalar_cache_t *cache = nullptr) const { if (unlikely (inner >= itemCount)) return 0.; + bool is_long = longWords (); + unsigned int count = regionIndices.len; + unsigned word_count = wordCount (); + unsigned int scount = is_long ? count : word_count; + unsigned int lcount = is_long ? word_count : 0; - unsigned int count = regionIndices.len; - bool is_long = longWords (); - unsigned word_count = wordCount (); - unsigned int scount = is_long ? count : word_count; - unsigned int lcount = is_long ? word_count : 0; + const HBUINT8 *bytes = get_delta_bytes (); + const HBUINT8 *row = bytes + inner * get_row_size (); - const HBUINT8 *bytes = get_delta_bytes (); - const HBUINT8 *row = bytes + inner * get_row_size (); + float delta = 0.; + unsigned int i = 0; - float delta = 0.; - unsigned int i = 0; + const HBINT32 *lcursor = reinterpret_cast (row); + for (; i < lcount; i++) + { + float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); + if (scalar) + delta += scalar * *lcursor; + lcursor++; + } + const HBINT16 *scursor = reinterpret_cast (lcursor); + for (; i < scount; i++) + { + float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); + if (scalar) + delta += scalar * *scursor; + scursor++; + } + const HBINT8 *bcursor = reinterpret_cast (scursor); + for (; i < count; i++) + { + float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); + if (scalar) + delta += scalar * *bcursor; + bcursor++; + } - const HBINT32 *lcursor = reinterpret_cast (row); - for (; i < lcount; i++) - { - float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); - delta += scalar * *lcursor++; - } - const HBINT16 *scursor = reinterpret_cast (lcursor); - for (; i < scount; i++) - { - float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); - delta += scalar * *scursor++; - } - const HBINT8 *bcursor = reinterpret_cast (scursor); - for (; i < count; i++) - { - float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); - delta += scalar * *bcursor++; - } + return delta; + } - return delta; + HB_ALWAYS_INLINE + float get_delta (unsigned int inner, + const int *coords, unsigned int coord_count, + const VarRegionList ®ions, + hb_scalar_cache_t *cache = nullptr) const + { + unsigned int count = regionIndices.len; + if (!count) return 0.f; // This is quite common, so optimize it. + return _get_delta (inner, coords, coord_count, regions, cache); } void get_region_scalars (const int *coords, unsigned int coord_count, @@ -3150,7 +3250,7 @@ struct MultiVarData const int *coords, unsigned int coord_count, const SparseVarRegionList ®ions, hb_array_t out, - SparseVarRegionList::cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { auto &deltaSets = StructAfter (regionIndices); @@ -3187,31 +3287,24 @@ struct MultiVarData struct ItemVariationStore { friend struct item_variations_t; - using cache_t = VarRegionList::cache_t; - cache_t *create_cache () const + hb_scalar_cache_t *create_cache () const { #ifdef HB_NO_VAR - return nullptr; + return hb_scalar_cache_t::create (0); #endif - unsigned count = (this+regions).regionCount; - if (!count) return nullptr; - - cache_t *cache = (cache_t *) hb_malloc (sizeof (float) * count); - if (unlikely (!cache)) return nullptr; - - for (unsigned i = 0; i < count; i++) - cache[i] = REGION_CACHE_ITEM_CACHE_INVALID; - - return cache; + return hb_scalar_cache_t::create ((this+regions).regionCount); } - static void destroy_cache (cache_t *cache) { hb_free (cache); } + static void destroy_cache (hb_scalar_cache_t *cache) + { + hb_scalar_cache_t::destroy (cache); + } private: float get_delta (unsigned int outer, unsigned int inner, const int *coords, unsigned int coord_count, - VarRegionList::cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { #ifdef HB_NO_VAR return 0.f; @@ -3229,7 +3322,7 @@ struct ItemVariationStore public: float get_delta (unsigned int index, const int *coords, unsigned int coord_count, - VarRegionList::cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { unsigned int outer = index >> 16; unsigned int inner = index & 0xFFFF; @@ -3237,7 +3330,7 @@ struct ItemVariationStore } float get_delta (unsigned int index, hb_array_t coords, - VarRegionList::cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { return get_delta (index, coords.arrayZ, coords.length, @@ -3356,7 +3449,7 @@ struct ItemVariationStore for (unsigned i = 0; i < count; i++) { hb_inc_bimap_t *map = inner_maps.push (); - if (!c->propagate_error(inner_maps)) + if (unlikely (!c->propagate_error(inner_maps))) return_trace(nullptr); auto &data = this+dataSets[i]; @@ -3445,43 +3538,28 @@ struct ItemVariationStore struct MultiItemVariationStore { - using cache_t = SparseVarRegionList::cache_t; - - cache_t *create_cache (hb_array_t static_cache = hb_array_t ()) const + hb_scalar_cache_t *create_cache (hb_scalar_cache_t *static_cache = nullptr) const { #ifdef HB_NO_VAR - return nullptr; + return hb_scalar_cache_t::create (0); #endif auto &r = this+regions; unsigned count = r.regions.len; - cache_t *cache; - if (count <= static_cache.length) - cache = static_cache.arrayZ; - else - { - cache = (cache_t *) hb_malloc (sizeof (float) * count); - if (unlikely (!cache)) return nullptr; - } - - for (unsigned i = 0; i < count; i++) - cache[i] = REGION_CACHE_ITEM_CACHE_INVALID; - - return cache; + return hb_scalar_cache_t::create (count, static_cache); } - static void destroy_cache (cache_t *cache, - hb_array_t static_cache = hb_array_t ()) + static void destroy_cache (hb_scalar_cache_t *cache, + hb_scalar_cache_t *static_cache = nullptr) { - if (cache != static_cache.arrayZ) - hb_free (cache); + hb_scalar_cache_t::destroy (cache, static_cache); } private: void get_delta (unsigned int outer, unsigned int inner, const int *coords, unsigned int coord_count, hb_array_t out, - VarRegionList::cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { #ifdef HB_NO_VAR return; @@ -3501,7 +3579,7 @@ struct MultiItemVariationStore void get_delta (unsigned int index, const int *coords, unsigned int coord_count, hb_array_t out, - VarRegionList::cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { unsigned int outer = index >> 16; unsigned int inner = index & 0xFFFF; @@ -3510,7 +3588,7 @@ struct MultiItemVariationStore void get_delta (unsigned int index, hb_array_t coords, hb_array_t out, - VarRegionList::cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { return get_delta (index, coords.arrayZ, coords.length, @@ -3540,8 +3618,6 @@ struct MultiItemVariationStore DEFINE_SIZE_ARRAY_SIZED (8, dataSets); }; -#undef REGION_CACHE_ITEM_CACHE_INVALID - template struct DeltaSetIndexMapFormat01 { @@ -3592,13 +3668,19 @@ struct DeltaSetIndexMapFormat01 return_trace (true); } + HB_ALWAYS_INLINE uint32_t map (unsigned int v) const /* Returns 16.16 outer.inner. */ { /* If count is zero, pass value unchanged. This takes * care of direct mapping for advance map. */ if (!mapCount) return v; + return _map (v); + } + HB_HOT + uint32_t _map (unsigned int v) const /* Returns 16.16 outer.inner. */ + { if (v >= mapCount) v = mapCount - 1; @@ -3654,8 +3736,8 @@ struct DeltaSetIndexMap { TRACE_SERIALIZE (this); unsigned length = plan.get_output_map ().length; - u.format = length <= 0xFFFF ? 0 : 1; - switch (u.format) { + u.format.v = length <= 0xFFFF ? 0 : 1; + switch (u.format.v) { case 0: hb_barrier (); return_trace (u.format0.serialize (c, plan)); case 1: hb_barrier (); return_trace (u.format1.serialize (c, plan)); default:return_trace (false); @@ -3664,7 +3746,7 @@ struct DeltaSetIndexMap uint32_t map (unsigned v) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return (u.format0.map (v)); case 1: hb_barrier (); return (u.format1.map (v)); default:return v; @@ -3673,7 +3755,7 @@ struct DeltaSetIndexMap unsigned get_map_count () const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0.get_map_count (); case 1: hb_barrier (); return u.format1.get_map_count (); default:return 0; @@ -3682,7 +3764,7 @@ struct DeltaSetIndexMap unsigned get_width () const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0.get_width (); case 1: hb_barrier (); return u.format1.get_width (); default:return 0; @@ -3691,7 +3773,7 @@ struct DeltaSetIndexMap unsigned get_inner_bit_count () const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0.get_inner_bit_count (); case 1: hb_barrier (); return u.format1.get_inner_bit_count (); default:return 0; @@ -3701,9 +3783,9 @@ struct DeltaSetIndexMap bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return_trace (u.format0.sanitize (c)); case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); default:return_trace (true); @@ -3713,7 +3795,7 @@ struct DeltaSetIndexMap DeltaSetIndexMap* copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return_trace (reinterpret_cast (u.format0.copy (c))); case 1: hb_barrier (); return_trace (reinterpret_cast (u.format1.copy (c))); default:return_trace (nullptr); @@ -3722,12 +3804,12 @@ struct DeltaSetIndexMap protected: union { - HBUINT8 format; /* Format identifier */ + struct { HBUINT8 v; } format; /* Format identifier */ DeltaSetIndexMapFormat01 format0; DeltaSetIndexMapFormat01 format1; } u; public: - DEFINE_SIZE_UNION (1, format); + DEFINE_SIZE_UNION (1, format.v); }; @@ -3736,7 +3818,7 @@ struct ItemVarStoreInstancer ItemVarStoreInstancer (const ItemVariationStore *varStore_, const DeltaSetIndexMap *varIdxMap, hb_array_t coords, - VarRegionList::cache_t *cache = nullptr) : + hb_scalar_cache_t *cache = nullptr) : varStore (varStore_), varIdxMap (varIdxMap), coords (coords), cache (cache) { if (!varStore) @@ -3762,7 +3844,7 @@ struct ItemVarStoreInstancer const ItemVariationStore *varStore; const DeltaSetIndexMap *varIdxMap; hb_array_t coords; - VarRegionList::cache_t *cache; + hb_scalar_cache_t *cache; }; struct MultiItemVarStoreInstancer @@ -3770,7 +3852,7 @@ struct MultiItemVarStoreInstancer MultiItemVarStoreInstancer (const MultiItemVariationStore *varStore, const DeltaSetIndexMap *varIdxMap, hb_array_t coords, - SparseVarRegionList::cache_t *cache = nullptr) : + hb_scalar_cache_t *cache = nullptr) : varStore (varStore), varIdxMap (varIdxMap), coords (coords), cache (cache) { if (!varStore) @@ -3803,7 +3885,7 @@ struct MultiItemVarStoreInstancer const MultiItemVariationStore *varStore; const DeltaSetIndexMap *varIdxMap; hb_array_t coords; - SparseVarRegionList::cache_t *cache; + hb_scalar_cache_t *cache; }; @@ -4130,7 +4212,7 @@ struct Condition bool evaluate (const int *coords, unsigned int coord_len, Instancer *instancer) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.evaluate (coords, coord_len, instancer); case 2: hb_barrier (); return u.format2.evaluate (coords, coord_len, instancer); case 3: hb_barrier (); return u.format3.evaluate (coords, coord_len, instancer); @@ -4143,7 +4225,7 @@ struct Condition Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c, hb_map_t *condition_map /* OUT */) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.keep_with_variations (c, condition_map); // TODO(subset) default: c->apply = false; return KEEP_COND_WITH_VAR; @@ -4153,9 +4235,9 @@ struct Condition 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) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward (ds)...)); case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward (ds)...)); @@ -4168,9 +4250,9 @@ struct Condition bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); case 3: hb_barrier (); return_trace (u.format3.sanitize (c)); @@ -4182,7 +4264,7 @@ struct Condition protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ConditionAxisRange format1; ConditionValue format2; ConditionAnd format3; @@ -4190,7 +4272,7 @@ struct Condition ConditionNegate format5; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; template @@ -4353,7 +4435,7 @@ struct FeatureTableSubstitutionRecord if (unlikely (!s->extend_min (this))) return_trace (false); uint32_t *new_feature_idx; - if (!c->feature_index_map->has (feature_index, &new_feature_idx)) + if (!c->feature_map_w_duplicates->has (feature_index, &new_feature_idx)) return_trace (false); if (!s->check_assign (featureIndex, *new_feature_idx, HB_SERIALIZE_ERROR_INT_OVERFLOW)) @@ -4371,7 +4453,7 @@ struct FeatureTableSubstitutionRecord { TRACE_SUBSET (this); uint32_t *new_feature_index; - if (!c->feature_index_map->has (featureIndex, &new_feature_index)) + if (!c->feature_map_w_duplicates->has (featureIndex, &new_feature_index)) return_trace (false); auto *out = c->subset_context->serializer->embed (this); @@ -4645,7 +4727,7 @@ struct FeatureVariations int keep_up_to = -1; for (int i = varRecords.len - 1; i >= 0; i--) { - if (varRecords[i].intersects_features (this, l->feature_index_map)) { + if (varRecords[i].intersects_features (this, l->feature_map_w_duplicates)) { keep_up_to = i; break; } @@ -4783,13 +4865,13 @@ struct VariationDevice hb_position_t get_x_delta (hb_font_t *font, const ItemVariationStore &store, - ItemVariationStore::cache_t *store_cache = nullptr) const - { return !font->num_coords ? 0 : font->em_scalef_x (get_delta (font, store, store_cache)); } + hb_scalar_cache_t *store_cache = nullptr) const + { return !font->has_nonzero_coords ? 0 : font->em_scalef_x (get_delta (font, store, store_cache)); } hb_position_t get_y_delta (hb_font_t *font, const ItemVariationStore &store, - ItemVariationStore::cache_t *store_cache = nullptr) const - { return !font->num_coords ? 0 : font->em_scalef_y (get_delta (font, store, store_cache)); } + hb_scalar_cache_t *store_cache = nullptr) const + { return !font->has_nonzero_coords ? 0 : font->em_scalef_y (get_delta (font, store, store_cache)); } VariationDevice* copy (hb_serialize_context_t *c, const hb_hashmap_t> *layout_variation_idx_delta_map) const @@ -4823,9 +4905,9 @@ struct VariationDevice float get_delta (hb_font_t *font, const ItemVariationStore &store, - ItemVariationStore::cache_t *store_cache = nullptr) const + hb_scalar_cache_t *store_cache = nullptr) const { - return store.get_delta (varIdx, font->coords, font->num_coords, (ItemVariationStore::cache_t *) store_cache); + return store.get_delta (varIdx, font->coords, font->num_coords, store_cache); } protected: @@ -4850,7 +4932,7 @@ struct Device { hb_position_t get_x_delta (hb_font_t *font, const ItemVariationStore &store=Null (ItemVariationStore), - ItemVariationStore::cache_t *store_cache = nullptr) const + hb_scalar_cache_t *store_cache = nullptr) const { switch (u.b.format) { @@ -4868,7 +4950,7 @@ struct Device } hb_position_t get_y_delta (hb_font_t *font, const ItemVariationStore &store=Null (ItemVariationStore), - ItemVariationStore::cache_t *store_cache = nullptr) const + hb_scalar_cache_t *store_cache = nullptr) const { switch (u.b.format) { @@ -4954,6 +5036,18 @@ struct Device } } + bool is_variation_device () const + { + switch (u.b.format) { +#ifndef HB_NO_VAR + case 0x8000: + return true; +#endif + default: + return false; + } + } + protected: union { DeviceHeader b; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh index 0cfa139a260..f6aabc84ac9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh @@ -63,12 +63,20 @@ inline bool PosLookup::dispatch_recurse_func (hb_ot_apply c->set_lookup_index (lookup_index); c->set_lookup_props (l.get_props ()); + uint32_t stack_match_positions[8]; + hb_vector_t saved_match_positions; + saved_match_positions.set_storage (stack_match_positions); + hb_swap (c->match_positions, saved_match_positions); + bool ret = false; auto *accel = gpos->get_accel (lookup_index); - ret = accel && accel->apply (c, l.get_subtable_count (), false); + ret = accel && accel->apply (c, false); c->set_lookup_index (saved_lookup_index); c->set_lookup_props (saved_lookup_props); + + hb_swap (c->match_positions, saved_match_positions); + return ret; } #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh index fd8a68be02d..8d5e1f18638 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh @@ -76,12 +76,20 @@ inline bool SubstLookup::dispatch_recurse_func (hb_ot_app c->set_lookup_index (lookup_index); c->set_lookup_props (l.get_props ()); + uint32_t stack_match_positions[8]; + hb_vector_t saved_match_positions; + saved_match_positions.set_storage (stack_match_positions); + hb_swap (c->match_positions, saved_match_positions); + bool ret = false; auto *accel = gsub->get_accel (lookup_index); - ret = accel && accel->apply (c, l.get_subtable_count (), false); + ret = accel && accel->apply (c, false); c->set_lookup_index (saved_lookup_index); c->set_lookup_props (saved_lookup_props); + + hb_swap (c->match_positions, saved_match_positions); + return ret; } #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh index 072e5cdba26..05bbf3395bf 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh @@ -397,303 +397,311 @@ struct hb_collect_coverage_context_t : set_t *set; }; -struct hb_ot_apply_context_t : - hb_dispatch_context_t +struct matcher_t { - struct matcher_t + typedef bool (*match_func_t) (hb_glyph_info_t &info, unsigned value, const void *data); + + template + void init (const context_t *c, bool context_match = false) { - typedef bool (*match_func_t) (hb_glyph_info_t &info, unsigned value, const void *data); + set_match_func (nullptr, nullptr); + lookup_props = c->lookup_props; + /* Ignore ZWNJ if we are matching GPOS, or matching GSUB context and asked to. */ + ignore_zwnj = c->table_index == 1 || (context_match && c->auto_zwnj); + /* Ignore ZWJ if we are matching context, or asked to. */ + ignore_zwj = context_match || c->auto_zwj; + /* Ignore hidden glyphs (like CGJ) during GPOS. */ + ignore_hidden = c->table_index == 1; + mask = context_match ? -1 : c->lookup_mask; + /* Per syllable matching is only for GSUB. */ + per_syllable = c->table_index == 0 && c->per_syllable; + syllable = 0; + } - void set_ignore_zwnj (bool ignore_zwnj_) { ignore_zwnj = ignore_zwnj_; } - void set_ignore_zwj (bool ignore_zwj_) { ignore_zwj = ignore_zwj_; } - void set_ignore_hidden (bool ignore_hidden_) { ignore_hidden = ignore_hidden_; } - void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; } - void set_mask (hb_mask_t mask_) { mask = mask_; } - void set_per_syllable (bool per_syllable_) { per_syllable = per_syllable_; } - void set_syllable (uint8_t syllable_) { syllable = per_syllable ? syllable_ : 0; } - void set_match_func (match_func_t match_func_, - const void *match_data_) - { match_func = match_func_; match_data = match_data_; } + void set_match_func (match_func_t match_func_, + const void *match_data_) + { match_func = match_func_; match_data = match_data_; } - enum may_match_t { - MATCH_NO, - MATCH_YES, - MATCH_MAYBE - }; - -#ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE -#endif - may_match_t may_match (hb_glyph_info_t &info, - hb_codepoint_t glyph_data) const - { - if (!(info.mask & mask) || - (syllable && syllable != info.syllable ())) - return MATCH_NO; - - if (match_func) - return match_func (info, glyph_data, match_data) ? MATCH_YES : MATCH_NO; - - return MATCH_MAYBE; - } - - enum may_skip_t { - SKIP_NO, - SKIP_YES, - SKIP_MAYBE - }; - -#ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE -#endif - may_skip_t may_skip (const hb_ot_apply_context_t *c, - const hb_glyph_info_t &info) const - { - if (!c->check_glyph_property (&info, lookup_props)) - return SKIP_YES; - - if (unlikely (_hb_glyph_info_is_default_ignorable (&info) && - (ignore_zwnj || !_hb_glyph_info_is_zwnj (&info)) && - (ignore_zwj || !_hb_glyph_info_is_zwj (&info)) && - (ignore_hidden || !_hb_glyph_info_is_hidden (&info)))) - return SKIP_MAYBE; - - return SKIP_NO; - } - - protected: - unsigned int lookup_props = 0; - hb_mask_t mask = -1; - bool ignore_zwnj = false; - bool ignore_zwj = false; - bool ignore_hidden = false; - bool per_syllable = false; - uint8_t syllable = 0; - match_func_t match_func = nullptr; - const void *match_data = nullptr; + enum may_match_t { + MATCH_NO, + MATCH_YES, + MATCH_MAYBE }; - struct skipping_iterator_t +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + may_match_t may_match (hb_glyph_info_t &info, + hb_codepoint_t glyph_data) const { - void init (hb_ot_apply_context_t *c_, bool context_match = false) - { - c = c_; - end = c->buffer->len; - match_glyph_data16 = nullptr; + if (!(info.mask & mask) || + (per_syllable && syllable && syllable != info.syllable ())) + return MATCH_NO; + + if (match_func) + return match_func (info, glyph_data, match_data) ? MATCH_YES : MATCH_NO; + + return MATCH_MAYBE; + } + + enum may_skip_t { + SKIP_NO, + SKIP_YES, + SKIP_MAYBE + }; + + template +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + may_skip_t may_skip (const context_t *c, + const hb_glyph_info_t &info) const + { + if (!c->check_glyph_property (&info, lookup_props)) + return SKIP_YES; + + if (unlikely (_hb_glyph_info_is_default_ignorable (&info) && + (ignore_zwnj || !_hb_glyph_info_is_zwnj (&info)) && + (ignore_zwj || !_hb_glyph_info_is_zwj (&info)) && + (ignore_hidden || !_hb_glyph_info_is_hidden (&info)))) + return SKIP_MAYBE; + + return SKIP_NO; + } + + public: + unsigned int lookup_props = 0; + hb_mask_t mask = -1; + bool ignore_zwnj = false; + bool ignore_zwj = false; + bool ignore_hidden = false; + bool per_syllable = false; + uint8_t syllable = 0; + match_func_t match_func = nullptr; + const void *match_data = nullptr; +}; + +template +struct skipping_iterator_t +{ + void init (context_t *c_, bool context_match = false) + { + c = c_; + end = c->buffer->len; + match_glyph_data16 = nullptr; #ifndef HB_NO_BEYOND_64K - match_glyph_data24 = nullptr; + match_glyph_data24 = nullptr; #endif - matcher.set_match_func (nullptr, nullptr); - matcher.set_lookup_props (c->lookup_props); - /* Ignore ZWNJ if we are matching GPOS, or matching GSUB context and asked to. */ - matcher.set_ignore_zwnj (c->table_index == 1 || (context_match && c->auto_zwnj)); - /* Ignore ZWJ if we are matching context, or asked to. */ - matcher.set_ignore_zwj (context_match || c->auto_zwj); - /* Ignore hidden glyphs (like CGJ) during GPOS. */ - matcher.set_ignore_hidden (c->table_index == 1); - matcher.set_mask (context_match ? -1 : c->lookup_mask); - /* Per syllable matching is only for GSUB. */ - matcher.set_per_syllable (c->table_index == 0 && c->per_syllable); - matcher.set_syllable (0); - } - void set_lookup_props (unsigned int lookup_props) - { - matcher.set_lookup_props (lookup_props); - } - void set_match_func (matcher_t::match_func_t match_func_, - const void *match_data_) - { - matcher.set_match_func (match_func_, match_data_); - } - void set_glyph_data (const HBUINT16 glyph_data[]) - { - match_glyph_data16 = glyph_data; + matcher.init (c, context_match); + } + void set_lookup_props (unsigned int lookup_props) + { + matcher.lookup_props = lookup_props; + } + void set_match_func (matcher_t::match_func_t match_func_, + const void *match_data_) + { + matcher.set_match_func (match_func_, match_data_); + } + void set_glyph_data (const HBUINT16 glyph_data[]) + { + match_glyph_data16 = glyph_data; #ifndef HB_NO_BEYOND_64K - match_glyph_data24 = nullptr; + match_glyph_data24 = nullptr; #endif - } + } #ifndef HB_NO_BEYOND_64K - void set_glyph_data (const HBUINT24 glyph_data[]) - { - match_glyph_data16 = nullptr; - match_glyph_data24 = glyph_data; - } + void set_glyph_data (const HBUINT24 glyph_data[]) + { + match_glyph_data16 = nullptr; + match_glyph_data24 = glyph_data; + } #endif #ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE + HB_ALWAYS_INLINE #endif - void reset (unsigned int start_index_) - { - idx = start_index_; - end = c->buffer->len; - matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0); - } - -#ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE -#endif - void reset_fast (unsigned int start_index_) - { - // Doesn't set end or syllable. Used by GPOS which doesn't care / change. - idx = start_index_; - } - - void reject () - { - backup_glyph_data (); - } - - matcher_t::may_skip_t -#ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE -#endif - may_skip (const hb_glyph_info_t &info) const - { return matcher.may_skip (c, info); } - - enum match_t { - MATCH, - NOT_MATCH, - SKIP - }; - -#ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE -#endif - match_t match (hb_glyph_info_t &info) - { - matcher_t::may_skip_t skip = matcher.may_skip (c, info); - if (unlikely (skip == matcher_t::SKIP_YES)) - return SKIP; - - matcher_t::may_match_t match = matcher.may_match (info, get_glyph_data ()); - if (match == matcher_t::MATCH_YES || - (match == matcher_t::MATCH_MAYBE && - skip == matcher_t::SKIP_NO)) - return MATCH; - - if (skip == matcher_t::SKIP_NO) - return NOT_MATCH; - - return SKIP; + void reset (unsigned int start_index_) + { + // For GSUB forward iterator + idx = start_index_; + end = c->buffer->len; + matcher.syllable = c->buffer->cur().syllable(); + } + void reset_back (unsigned int start_index_, bool from_out_buffer = false) + { + // For GSUB backward iterator + idx = start_index_; + matcher.syllable = c->buffer->cur().syllable(); } #ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE + HB_ALWAYS_INLINE #endif - bool next (unsigned *unsafe_to = nullptr) - { - const signed stop = (signed) end - 1; - while ((signed) idx < stop) - { - idx++; - switch (match (c->buffer->info[idx])) - { - case MATCH: - { - advance_glyph_data (); - return true; - } - case NOT_MATCH: - { - if (unsafe_to) - *unsafe_to = idx + 1; - return false; - } - case SKIP: - continue; - } - } - if (unsafe_to) - *unsafe_to = end; - return false; - } + void reset_fast (unsigned int start_index_) + { + // Doesn't set end or syllable. Used by GPOS which doesn't care / change. + idx = start_index_; + } + #ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE + HB_ALWAYS_INLINE #endif - bool prev (unsigned *unsafe_from = nullptr) - { - const unsigned stop = 0; - while (idx > stop) - { - idx--; - switch (match (c->buffer->out_info[idx])) - { - case MATCH: - { - advance_glyph_data (); - return true; - } - case NOT_MATCH: - { - if (unsafe_from) - *unsafe_from = hb_max (1u, idx) - 1u; - return false; - } - case SKIP: - continue; - } - } - if (unsafe_from) - *unsafe_from = 0; - return false; - } + matcher_t::may_skip_t may_skip (const hb_glyph_info_t &info) const + { return matcher.may_skip (c, info); } - HB_ALWAYS_INLINE - hb_codepoint_t - get_glyph_data () - { - if (match_glyph_data16) return *match_glyph_data16; -#ifndef HB_NO_BEYOND_64K - else - if (match_glyph_data24) return *match_glyph_data24; -#endif - return 0; - } - HB_ALWAYS_INLINE - void - advance_glyph_data () - { - if (match_glyph_data16) match_glyph_data16++; -#ifndef HB_NO_BEYOND_64K - else - if (match_glyph_data24) match_glyph_data24++; -#endif - } - void - backup_glyph_data () - { - if (match_glyph_data16) match_glyph_data16--; -#ifndef HB_NO_BEYOND_64K - else - if (match_glyph_data24) match_glyph_data24--; -#endif - } - - unsigned int idx; - protected: - hb_ot_apply_context_t *c; - matcher_t matcher; - const HBUINT16 *match_glyph_data16; -#ifndef HB_NO_BEYOND_64K - const HBUINT24 *match_glyph_data24; -#endif - - unsigned int end; + enum match_t { + MATCH, + NOT_MATCH, + SKIP }; +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + match_t match (hb_glyph_info_t &info) + { + matcher_t::may_skip_t skip = matcher.may_skip (c, info); + if (unlikely (skip == matcher_t::SKIP_YES)) + return SKIP; + matcher_t::may_match_t match = matcher.may_match (info, get_glyph_data ()); + if (match == matcher_t::MATCH_YES || + (match == matcher_t::MATCH_MAYBE && + skip == matcher_t::SKIP_NO)) + return MATCH; + + if (skip == matcher_t::SKIP_NO) + return NOT_MATCH; + + return SKIP; + } + +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + bool next (unsigned *unsafe_to = nullptr) + { + auto *info = c->buffer->info; + const signed stop = (signed) end - 1; + while ((signed) idx < stop) + { + idx++; + switch (match (info[idx])) + { + case MATCH: + { + advance_glyph_data (); + return true; + } + case NOT_MATCH: + { + if (unsafe_to) + *unsafe_to = idx + 1; + return false; + } + case SKIP: + continue; + } + } + if (unsafe_to) + *unsafe_to = end; + return false; + } +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + bool prev (unsigned *unsafe_from = nullptr) + { + auto *out_info = c->buffer->out_info; + const unsigned stop = 0; + while (idx > stop) + { + idx--; + switch (match (out_info[idx])) + { + case MATCH: + { + advance_glyph_data (); + return true; + } + case NOT_MATCH: + { + if (unsafe_from) + *unsafe_from = hb_max (1u, idx) - 1u; + return false; + } + case SKIP: + continue; + } + } + if (unsafe_from) + *unsafe_from = 0; + return false; + } + + HB_ALWAYS_INLINE + hb_codepoint_t + get_glyph_data () + { + if (match_glyph_data16) return *match_glyph_data16; +#ifndef HB_NO_BEYOND_64K + else + if (match_glyph_data24) return *match_glyph_data24; +#endif + return 0; + } + HB_ALWAYS_INLINE + void + advance_glyph_data () + { + if (match_glyph_data16) match_glyph_data16++; +#ifndef HB_NO_BEYOND_64K + else + if (match_glyph_data24) match_glyph_data24++; +#endif + } + + unsigned int idx; + protected: + context_t *c; + matcher_t matcher; + const HBUINT16 *match_glyph_data16; +#ifndef HB_NO_BEYOND_64K + const HBUINT24 *match_glyph_data24; +#endif + + unsigned int end; +}; + +struct hb_ot_apply_context_t : + hb_dispatch_context_t +{ const char *get_name () { return "APPLY"; } typedef return_t (*recurse_func_t) (hb_ot_apply_context_t *c, unsigned int lookup_index); + template - return_t dispatch (const T &obj) { return obj.apply (this); } + static inline auto apply_ (const T &obj, hb_ot_apply_context_t *c, hb_priority<1>) HB_RETURN (return_t, obj.apply (c, nullptr) ) + template + static inline auto apply_ (const T &obj, hb_ot_apply_context_t *c, hb_priority<0>) HB_RETURN (return_t, obj.apply (c) ) + template + return_t dispatch (const T &obj) { return apply_(obj, this, hb_prioritize); } + static return_t default_return_value () { return false; } bool stop_sublookup_iteration (return_t r) const { return r; } return_t recurse (unsigned int sub_lookup_index) { - if (unlikely (nesting_level_left == 0 || !recurse_func || buffer->max_ops-- <= 0)) + assert (recurse_func); + if (unlikely (nesting_level_left == 0)) { - buffer->shaping_failed = true; + buffer->successful = false; + return default_return_value (); + } + + buffer->max_ops--; + if (unlikely (buffer->max_ops < 0)) + { + buffer->successful = false; return default_return_value (); } @@ -703,7 +711,7 @@ struct hb_ot_apply_context_t : return ret; } - skipping_iterator_t iter_input, iter_context; + skipping_iterator_t iter_input, iter_context; unsigned int table_index; /* GSUB/GPOS */ hb_font_t *font; @@ -715,8 +723,7 @@ struct hb_ot_apply_context_t : const GDEF::accelerator_t &gdef_accel; const hb_ot_layout_lookup_accelerator_t *lookup_accel = nullptr; const ItemVariationStore &var_store; - ItemVariationStore::cache_t *var_store_cache; - hb_set_digest_t digest; + hb_scalar_cache_t *var_store_cache; hb_direction_t direction; hb_mask_t lookup_mask = 1; @@ -734,11 +741,14 @@ struct hb_ot_apply_context_t : signed last_base = -1; // GPOS uses unsigned last_base_until = 0; // GPOS uses + hb_vector_t match_positions; + uint32_t stack_match_positions[8]; + hb_ot_apply_context_t (unsigned int table_index_, hb_font_t *font_, hb_buffer_t *buffer_, hb_blob_t *table_blob_, - ItemVariationStore::cache_t *var_store_cache_ = nullptr) : + hb_scalar_cache_t *var_store_cache_ = nullptr) : table_index (table_index_), font (font_), face (font->face), buffer (buffer_), sanitizer (table_blob_), @@ -762,7 +772,7 @@ struct hb_ot_apply_context_t : has_glyph_classes (gdef.has_glyph_classes ()) { init_iters (); - buffer->collect_codepoints (digest); + match_positions.set_storage (stack_match_positions); } void init_iters () @@ -778,7 +788,11 @@ struct hb_ot_apply_context_t : void set_random (bool random_) { random = random_; } void set_recurse_func (recurse_func_t func) { recurse_func = func; } void set_lookup_index (unsigned int lookup_index_) { lookup_index = lookup_index_; } - void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; init_iters (); } + void set_lookup_props (unsigned int lookup_props_) + { + lookup_props = gdef_accel.sanitize_lookup_props (lookup_props_); + init_iters (); + } uint32_t random_number () { @@ -787,7 +801,9 @@ struct hb_ot_apply_context_t : return buffer->random_state; } - bool match_properties_mark (hb_codepoint_t glyph, + HB_ALWAYS_INLINE + HB_HOT + bool match_properties_mark (const hb_glyph_info_t *info, unsigned int glyph_props, unsigned int match_props) const { @@ -795,7 +811,7 @@ struct hb_ot_apply_context_t : * match_props has the set index. */ if (match_props & LookupFlag::UseMarkFilteringSet) - return gdef_accel.mark_set_covers (match_props >> 16, glyph); + return gdef_accel.mark_set_covers (match_props >> 16, info->codepoint); /* The second byte of match_props has the meaning * "ignore marks of attachment type different than @@ -811,7 +827,7 @@ struct hb_ot_apply_context_t : HB_ALWAYS_INLINE #endif bool check_glyph_property (const hb_glyph_info_t *info, - unsigned int match_props) const + unsigned match_props) const { unsigned int glyph_props = _hb_glyph_info_get_glyph_props (info); @@ -821,8 +837,8 @@ struct hb_ot_apply_context_t : if (glyph_props & match_props & LookupFlag::IgnoreFlags) return false; - if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_PROPS_MARK)) - return match_properties_mark (info->codepoint, glyph_props, match_props); + if (glyph_props & HB_OT_LAYOUT_GLYPH_PROPS_MARK) + return match_properties_mark (info, glyph_props, match_props); return true; } @@ -832,7 +848,7 @@ struct hb_ot_apply_context_t : bool ligature = false, bool component = false) { - digest.add (glyph_index); + buffer->digest.add (glyph_index); if (new_syllables != (unsigned) -1) buffer->cur().syllable() = new_syllables; @@ -890,54 +906,58 @@ struct hb_ot_apply_context_t : } }; -enum class hb_ot_lookup_cache_op_t +enum class hb_ot_subtable_cache_op_t { - CREATE, ENTER, LEAVE, - DESTROY, }; struct hb_accelerate_subtables_context_t : hb_dispatch_context_t { - template - static inline bool apply_to (const void *obj, hb_ot_apply_context_t *c) + template + static inline auto apply_ (const T *obj, hb_ot_apply_context_t *c, void *external_cache, hb_priority<1>) HB_RETURN (bool, obj->apply (c, external_cache) ) + template + static inline auto apply_ (const T *obj, hb_ot_apply_context_t *c, void *external_cache, hb_priority<0>) HB_RETURN (bool, obj->apply (c) ) + template + static inline bool apply_to (const void *obj, hb_ot_apply_context_t *c, void *external_cache) { - const Type *typed_obj = (const Type *) obj; - return typed_obj->apply (c); + const T *typed_obj = (const T *) obj; + return apply_ (typed_obj, c, external_cache, hb_prioritize); } #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE template - static inline auto apply_cached_ (const T *obj, hb_ot_apply_context_t *c, hb_priority<1>) HB_RETURN (bool, obj->apply_cached (c) ) + static inline auto apply_cached_ (const T *obj, hb_ot_apply_context_t *c, void *external_cache, hb_priority<2>) HB_RETURN (bool, obj->apply_cached (c, external_cache) ) template - static inline auto apply_cached_ (const T *obj, hb_ot_apply_context_t *c, hb_priority<0>) HB_RETURN (bool, obj->apply (c) ) - template - static inline bool apply_cached_to (const void *obj, hb_ot_apply_context_t *c) + static inline auto apply_cached_ (const T *obj, hb_ot_apply_context_t *c, void *external_cache, hb_priority<1>) HB_RETURN (bool, obj->apply (c, external_cache) ) + template + static inline auto apply_cached_ (const T *obj, hb_ot_apply_context_t *c, void *external_cache, hb_priority<0>) HB_RETURN (bool, obj->apply (c) ) + template + static inline bool apply_cached_to (const void *obj, hb_ot_apply_context_t *c, void *external_cache) { - const Type *typed_obj = (const Type *) obj; - return apply_cached_ (typed_obj, c, hb_prioritize); + const T *typed_obj = (const T *) obj; + return apply_cached_ (typed_obj, c, external_cache, hb_prioritize); } template - static inline auto cache_func_ (void *p, - hb_ot_lookup_cache_op_t op, - hb_priority<1>) HB_RETURN (void *, T::cache_func (p, op) ) + static inline auto cache_func_ (hb_ot_apply_context_t *c, + hb_ot_subtable_cache_op_t op, + hb_priority<1>) HB_RETURN (bool, T::cache_func (c, op) ) template - static inline void * cache_func_ (void *p, - hb_ot_lookup_cache_op_t op HB_UNUSED, - hb_priority<0>) { return (void *) false; } + static inline bool cache_func_ (hb_ot_apply_context_t *c, + hb_ot_subtable_cache_op_t op HB_UNUSED, + hb_priority<0>) { return false; } template - static inline void * cache_func_to (void *p, - hb_ot_lookup_cache_op_t op) + static inline bool cache_func_to (hb_ot_apply_context_t *c, + hb_ot_subtable_cache_op_t op) { - return cache_func_ (p, op, hb_prioritize); + return cache_func_ (c, op, hb_prioritize); } #endif - typedef bool (*hb_apply_func_t) (const void *obj, hb_ot_apply_context_t *c); - typedef void * (*hb_cache_func_t) (void *p, hb_ot_lookup_cache_op_t op); + typedef bool (*hb_apply_func_t) (const void *obj, hb_ot_apply_context_t *c, void *external_cache); + typedef bool (*hb_cache_func_t) (hb_ot_apply_context_t *c, hb_ot_subtable_cache_op_t op); struct hb_applicable_t { @@ -950,6 +970,7 @@ struct hb_accelerate_subtables_context_t : #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE , hb_apply_func_t apply_cached_func_ , hb_cache_func_t cache_func_ + , void *external_cache_ #endif ) { @@ -958,27 +979,34 @@ struct hb_accelerate_subtables_context_t : #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE apply_cached_func = apply_cached_func_; cache_func = cache_func_; + external_cache = external_cache_; #endif digest.init (); obj_.get_coverage ().collect_coverage (&digest); } +#ifdef HB_NO_OT_LAYOUT_LOOKUP_CACHE bool apply (hb_ot_apply_context_t *c) const { - return digest.may_have (c->buffer->cur().codepoint) && apply_func (obj, c); + return digest.may_have (c->buffer->cur().codepoint) && apply_func (obj, c, nullptr); + } +#else + bool apply (hb_ot_apply_context_t *c) const + { + return digest.may_have (c->buffer->cur().codepoint) && apply_func (obj, c, external_cache); } -#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE bool apply_cached (hb_ot_apply_context_t *c) const { - return digest.may_have (c->buffer->cur().codepoint) && apply_cached_func (obj, c); + return digest.may_have (c->buffer->cur().codepoint) && apply_cached_func (obj, c, external_cache); } + bool cache_enter (hb_ot_apply_context_t *c) const { - return (bool) cache_func (c, hb_ot_lookup_cache_op_t::ENTER); + return cache_func (c, hb_ot_subtable_cache_op_t::ENTER); } void cache_leave (hb_ot_apply_context_t *c) const { - cache_func (c, hb_ot_lookup_cache_op_t::LEAVE); + cache_func (c, hb_ot_subtable_cache_op_t::LEAVE); } #endif @@ -988,6 +1016,7 @@ struct hb_accelerate_subtables_context_t : #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE hb_apply_func_t apply_cached_func; hb_cache_func_t cache_func; + void *external_cache; #endif hb_set_digest_t digest; }; @@ -997,12 +1026,23 @@ struct hb_accelerate_subtables_context_t : auto cache_cost (const T &obj, hb_priority<1>) HB_AUTO_RETURN ( obj.cache_cost () ) template auto cache_cost (const T &obj, hb_priority<0>) HB_AUTO_RETURN ( 0u ) + + template + auto external_cache_create (const T &obj, hb_priority<1>) HB_AUTO_RETURN ( obj.external_cache_create () ) + template + auto external_cache_create (const T &obj, hb_priority<0>) HB_AUTO_RETURN ( nullptr ) #endif /* Dispatch interface. */ template return_t dispatch (const T &obj) { +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + void *external_cache = nullptr; + if (i < 8) + external_cache = external_cache_create (obj, hb_prioritize); +#endif + hb_applicable_t *entry = &array[i++]; entry->init (obj, @@ -1010,6 +1050,7 @@ struct hb_accelerate_subtables_context_t : #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE , apply_cached_to , cache_func_to + , external_cache #endif ); @@ -1023,10 +1064,10 @@ struct hb_accelerate_subtables_context_t : * and we allocate the cache opportunity to the costliest subtable. */ unsigned cost = cache_cost (obj, hb_prioritize); - if (cost > cache_user_cost) + if (cost > subtable_cache_user_cost) { - cache_user_idx = i - 1; - cache_user_cost = cost; + subtable_cache_user_idx = i - 1; + subtable_cache_user_cost = cost; } #endif @@ -1041,8 +1082,8 @@ struct hb_accelerate_subtables_context_t : unsigned i = 0; #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - unsigned cache_user_idx = (unsigned) -1; - unsigned cache_user_cost = 0; + unsigned subtable_cache_user_idx = (unsigned) -1; + unsigned subtable_cache_user_cost = 0; #endif }; @@ -1191,38 +1232,50 @@ static inline bool match_class (hb_glyph_info_t &info, unsigned value, const voi const ClassDef &class_def = *reinterpret_cast(data); return class_def.get_class (info.codepoint) == value; } -static inline bool match_class_cached (hb_glyph_info_t &info, unsigned value, const void *data) +static inline unsigned get_class_cached (const ClassDef &class_def, hb_glyph_info_t &info) { unsigned klass = info.syllable(); if (klass < 255) - return klass == value; - const ClassDef &class_def = *reinterpret_cast(data); + return klass; klass = class_def.get_class (info.codepoint); if (likely (klass < 255)) info.syllable() = klass; - return klass == value; + return klass; } -static inline bool match_class_cached1 (hb_glyph_info_t &info, unsigned value, const void *data) +static inline bool match_class_cached (hb_glyph_info_t &info, unsigned value, const void *data) +{ + const ClassDef &class_def = *reinterpret_cast(data); + return get_class_cached (class_def, info) == value; +} +static inline unsigned get_class_cached1 (const ClassDef &class_def, hb_glyph_info_t &info) { unsigned klass = info.syllable() & 0x0F; if (klass < 15) - return klass == value; - const ClassDef &class_def = *reinterpret_cast(data); + return klass; klass = class_def.get_class (info.codepoint); if (likely (klass < 15)) info.syllable() = (info.syllable() & 0xF0) | klass; - return klass == value; + return klass; } -static inline bool match_class_cached2 (hb_glyph_info_t &info, unsigned value, const void *data) +static inline bool match_class_cached1 (hb_glyph_info_t &info, unsigned value, const void *data) +{ + const ClassDef &class_def = *reinterpret_cast(data); + return get_class_cached1 (class_def, info) == value; +} +static inline unsigned get_class_cached2 (const ClassDef &class_def, hb_glyph_info_t &info) { unsigned klass = (info.syllable() & 0xF0) >> 4; if (klass < 15) - return klass == value; - const ClassDef &class_def = *reinterpret_cast(data); + return klass; klass = class_def.get_class (info.codepoint); if (likely (klass < 15)) info.syllable() = (info.syllable() & 0x0F) | (klass << 4); - return klass == value; + return klass; +} +static inline bool match_class_cached2 (hb_glyph_info_t &info, unsigned value, const void *data) +{ + const ClassDef &class_def = *reinterpret_cast(data); + return get_class_cached2 (class_def, info) == value; } static inline bool match_coverage (hb_glyph_info_t &info, unsigned value, const void *data) { @@ -1261,16 +1314,24 @@ static bool match_input (hb_ot_apply_context_t *c, match_func_t match_func, const void *match_data, unsigned int *end_position, - unsigned int *match_positions, unsigned int *p_total_component_count = nullptr) { TRACE_APPLY (nullptr); - if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return_trace (false); - hb_buffer_t *buffer = c->buffer; - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + if (count == 1) + { + *end_position = buffer->idx + 1; + c->match_positions[0] = buffer->idx; + if (p_total_component_count) + *p_total_component_count = _hb_glyph_info_get_lig_num_comps (&buffer->cur()); + return_trace (true); + } + + if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return_trace (false); + + auto &skippy_iter = c->iter_input; skippy_iter.reset (buffer->idx); skippy_iter.set_match_func (match_func, match_data); skippy_iter.set_glyph_data (input); @@ -1319,7 +1380,10 @@ static bool match_input (hb_ot_apply_context_t *c, return_trace (false); } - match_positions[i] = skippy_iter.idx; + if (unlikely (i + 1 > c->match_positions.length && + !c->match_positions.resize_dirty (i + 1))) + return_trace (false); + c->match_positions.arrayZ[i] = skippy_iter.idx; unsigned int this_lig_id = _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]); unsigned int this_lig_comp = _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]); @@ -1349,7 +1413,7 @@ static bool match_input (hb_ot_apply_context_t *c, j--; } - if (found && skippy_iter.may_skip (out[j]) == hb_ot_apply_context_t::matcher_t::SKIP_YES) + if (found && skippy_iter.may_skip (out[j]) == matcher_t::SKIP_YES) ligbase = LIGBASE_MAY_SKIP; else ligbase = LIGBASE_MAY_NOT_SKIP; @@ -1379,13 +1443,12 @@ static bool match_input (hb_ot_apply_context_t *c, *p_total_component_count = total_component_count; } - match_positions[0] = buffer->idx; + c->match_positions.arrayZ[0] = buffer->idx; return_trace (true); } static inline bool ligate_input (hb_ot_apply_context_t *c, unsigned int count, /* Including the first glyph */ - const unsigned int *match_positions, /* Including the first glyph */ unsigned int match_end, hb_codepoint_t lig_glyph, unsigned int total_component_count) @@ -1428,10 +1491,10 @@ static inline bool ligate_input (hb_ot_apply_context_t *c, * https://bugzilla.gnome.org/show_bug.cgi?id=437633 */ - bool is_base_ligature = _hb_glyph_info_is_base_glyph (&buffer->info[match_positions[0]]); - bool is_mark_ligature = _hb_glyph_info_is_mark (&buffer->info[match_positions[0]]); + bool is_base_ligature = _hb_glyph_info_is_base_glyph (&buffer->info[c->match_positions.arrayZ[0]]); + bool is_mark_ligature = _hb_glyph_info_is_mark (&buffer->info[c->match_positions.arrayZ[0]]); for (unsigned int i = 1; i < count; i++) - if (!_hb_glyph_info_is_mark (&buffer->info[match_positions[i]])) + if (!_hb_glyph_info_is_mark (&buffer->info[c->match_positions.arrayZ[i]])) { is_base_ligature = false; is_mark_ligature = false; @@ -1457,7 +1520,7 @@ static inline bool ligate_input (hb_ot_apply_context_t *c, for (unsigned int i = 1; i < count; i++) { - while (buffer->idx < match_positions[i] && buffer->successful) + while (buffer->idx < c->match_positions.arrayZ[i] && buffer->successful) { if (is_ligature) { @@ -1512,8 +1575,14 @@ static bool match_backtrack (hb_ot_apply_context_t *c, { TRACE_APPLY (nullptr); - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context; - skippy_iter.reset (c->buffer->backtrack_len ()); + if (!count) + { + *match_start = c->buffer->backtrack_len (); + return_trace (true); + } + + auto &skippy_iter = c->iter_context; + skippy_iter.reset_back (c->buffer->backtrack_len ()); skippy_iter.set_match_func (match_func, match_data); skippy_iter.set_glyph_data (backtrack); @@ -1545,7 +1614,13 @@ static bool match_lookahead (hb_ot_apply_context_t *c, { TRACE_APPLY (nullptr); - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context; + if (!count) + { + *end_index = start_index; + return_trace (true); + } + + auto &skippy_iter = c->iter_context; assert (start_index >= 1); skippy_iter.reset (start_index - 1); skippy_iter.set_match_func (match_func, match_data); @@ -1696,7 +1771,6 @@ static inline void recurse_lookups (context_t *c, static inline void apply_lookup (hb_ot_apply_context_t *c, unsigned int count, /* Including the first glyph */ - unsigned int *match_positions, /* Including the first glyph */ unsigned int lookupCount, const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */ unsigned int match_end) @@ -1704,9 +1778,6 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, hb_buffer_t *buffer = c->buffer; int end; - unsigned int *match_positions_input = match_positions; - unsigned int match_positions_count = count; - /* All positions are distance from beginning of *output* buffer. * Adjust. */ { @@ -1716,7 +1787,7 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, int delta = bl - buffer->idx; /* Convert positions to new indexing. */ for (unsigned int j = 0; j < count; j++) - match_positions[j] += delta; + c->match_positions.arrayZ[j] += delta; } for (unsigned int i = 0; i < lookupCount && buffer->successful; i++) @@ -1728,10 +1799,10 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, unsigned int orig_len = buffer->backtrack_len () + buffer->lookahead_len (); /* This can happen if earlier recursed lookups deleted many entries. */ - if (unlikely (match_positions[idx] >= orig_len)) + if (unlikely (c->match_positions.arrayZ[idx] >= orig_len)) continue; - if (unlikely (!buffer->move_to (match_positions[idx]))) + if (unlikely (!buffer->move_to (c->match_positions.arrayZ[idx]))) break; if (unlikely (buffer->max_ops <= 0)) @@ -1790,9 +1861,9 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, */ end += delta; - if (end < int (match_positions[idx])) + if (end < int (c->match_positions.arrayZ[idx])) { - /* End might end up being smaller than match_positions[idx] if the recursed + /* End might end up being smaller than match_positions.arrayZ[idx] if the recursed * lookup ended up removing many items. * Just never rewind end beyond start of current position, since that is * not possible in the recursed lookup. Also adjust delta as such. @@ -1800,8 +1871,8 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, * https://bugs.chromium.org/p/chromium/issues/detail?id=659496 * https://github.com/harfbuzz/harfbuzz/issues/1611 */ - delta += match_positions[idx] - end; - end = match_positions[idx]; + delta += c->match_positions.arrayZ[idx] - end; + end = c->match_positions.arrayZ[idx]; } unsigned int next = idx + 1; /* next now is the position after the recursed lookup. */ @@ -1810,27 +1881,9 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, { if (unlikely (delta + count > HB_MAX_CONTEXT_LENGTH)) break; - if (unlikely (delta + count > match_positions_count)) - { - unsigned new_match_positions_count = hb_max (delta + count, hb_max(match_positions_count, 4u) * 1.5); - if (match_positions == match_positions_input) - { - match_positions = (unsigned int *) hb_malloc (new_match_positions_count * sizeof (match_positions[0])); - if (unlikely (!match_positions)) - break; - memcpy (match_positions, match_positions_input, count * sizeof (match_positions[0])); - match_positions_count = new_match_positions_count; - } - else - { - unsigned int *new_match_positions = (unsigned int *) hb_realloc (match_positions, new_match_positions_count * sizeof (match_positions[0])); - if (unlikely (!new_match_positions)) - break; - match_positions = new_match_positions; - match_positions_count = new_match_positions_count; - } - } - + if (unlikely (count + delta > c->match_positions.length && + !c->match_positions.resize_dirty (count + delta))) + return; } else { @@ -1840,23 +1893,20 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, } /* Shift! */ - memmove (match_positions + next + delta, match_positions + next, - (count - next) * sizeof (match_positions[0])); + memmove (c->match_positions + next + delta, c->match_positions + next, + (count - next) * sizeof (c->match_positions.arrayZ[0])); next += delta; count += delta; /* Fill in new entries. */ for (unsigned int j = idx + 1; j < next; j++) - match_positions[j] = match_positions[j - 1] + 1; + c->match_positions.arrayZ[j] = c->match_positions.arrayZ[j - 1] + 1; /* And fixup the rest. */ for (; next < count; next++) - match_positions[next] += delta; + c->match_positions.arrayZ[next] += delta; } - if (match_positions != match_positions_input) - hb_free (match_positions); - assert (end >= 0); (void) buffer->move_to (end); } @@ -1950,34 +2000,25 @@ static inline bool context_would_apply_lookup (hb_would_apply_context_t *c, } template -HB_ALWAYS_INLINE -static bool context_apply_lookup (hb_ot_apply_context_t *c, - unsigned int inputCount, /* Including the first glyph (not matched) */ - const HBUINT input[], /* Array of input values--start with second glyph */ - unsigned int lookupCount, - const LookupRecord lookupRecord[], - const ContextApplyLookupContext &lookup_context) +static inline bool context_apply_lookup (hb_ot_apply_context_t *c, + unsigned int inputCount, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + unsigned int lookupCount, + const LookupRecord lookupRecord[], + const ContextApplyLookupContext &lookup_context) { if (unlikely (inputCount > HB_MAX_CONTEXT_LENGTH)) return false; - unsigned match_positions_stack[4]; - unsigned *match_positions = match_positions_stack; - if (unlikely (inputCount > ARRAY_LENGTH (match_positions_stack))) - { - match_positions = (unsigned *) hb_malloc (hb_max (inputCount, 1u) * sizeof (match_positions[0])); - if (unlikely (!match_positions)) - return false; - } unsigned match_end = 0; bool ret = false; if (match_input (c, inputCount, input, lookup_context.funcs.match, lookup_context.match_data, - &match_end, match_positions)) + &match_end)) { c->buffer->unsafe_to_break (c->buffer->idx, match_end); apply_lookup (c, - inputCount, match_positions, + inputCount, lookupCount, lookupRecord, match_end); ret = true; @@ -1988,12 +2029,34 @@ static bool context_apply_lookup (hb_ot_apply_context_t *c, ret = false; } - if (unlikely (match_positions != match_positions_stack)) - hb_free (match_positions); - return ret; } +static inline bool context_cache_func (hb_ot_apply_context_t *c, hb_ot_subtable_cache_op_t op) +{ + switch (op) + { + case hb_ot_subtable_cache_op_t::ENTER: + { + if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable)) + return false; + auto &info = c->buffer->info; + unsigned count = c->buffer->len; + for (unsigned i = 0; i < count; i++) + info[i].syllable() = 255; + c->new_syllables = 255; + return true; + } + case hb_ot_subtable_cache_op_t::LEAVE: + { + c->new_syllables = (unsigned) -1; + HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable); + break; + } + } + return false; +} + template struct Rule { @@ -2209,24 +2272,29 @@ struct RuleSet * * Replicated from LigatureSet::apply(). */ - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + /* We use the iter_context instead of iter_input, to avoid skipping + * default-ignorables and such. + * + * Related: https://github.com/harfbuzz/harfbuzz/issues/4813 + */ + auto &skippy_iter = c->iter_context; skippy_iter.reset (c->buffer->idx); skippy_iter.set_match_func (match_always, nullptr); skippy_iter.set_glyph_data ((HBUINT16 *) nullptr); - unsigned unsafe_to = (unsigned) -1, unsafe_to1 = 0, unsafe_to2 = 0; + unsigned unsafe_to = (unsigned) -1, unsafe_to1, unsafe_to2 = 0; hb_glyph_info_t *first = nullptr, *second = nullptr; bool matched = skippy_iter.next (); if (likely (matched)) { - first = &c->buffer->info[skippy_iter.idx]; - unsafe_to = skippy_iter.idx + 1; - if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx])) { /* Can't use the fast path if eg. the next char is a default-ignorable * or other skippable. */ goto slow; } + + first = &c->buffer->info[skippy_iter.idx]; + unsafe_to1 = skippy_iter.idx + 1; } else { @@ -2242,8 +2310,15 @@ struct RuleSet ; } matched = skippy_iter.next (); - if (likely (matched && !skippy_iter.may_skip (c->buffer->info[skippy_iter.idx]))) + if (likely (matched)) { + if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx])) + { + /* Can't use the fast path if eg. the next char is a default-ignorable + * or other skippable. */ + goto slow; + } + second = &c->buffer->info[skippy_iter.idx]; unsafe_to2 = skippy_iter.idx + 1; } @@ -2280,6 +2355,15 @@ struct RuleSet { if (unsafe_to == (unsigned) -1) unsafe_to = unsafe_to1; + + // Skip ahead to next possible first glyph match. + for (; i + 1 < num_rules; i++) + { + const auto &r2 = this+rule.arrayZ[i + 1]; + const auto &input2 = r2.inputZ; + if (r2.inputCount <= 1 || input2.arrayZ[0] != input.arrayZ[0]) + break; + } } } if (likely (unsafe_to != (unsigned) -1)) @@ -2622,46 +2706,37 @@ struct ContextFormat2_5 unsigned cache_cost () const { - unsigned c = (this+classDef).cost () * ruleSet.len; - return c >= 4 ? c : 0; + return (this+classDef).cost (); } - static void * cache_func (void *p, hb_ot_lookup_cache_op_t op) + static bool cache_func (hb_ot_apply_context_t *c, hb_ot_subtable_cache_op_t op) { - switch (op) - { - case hb_ot_lookup_cache_op_t::CREATE: - return (void *) true; - case hb_ot_lookup_cache_op_t::ENTER: - { - hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p; - if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable)) - return (void *) false; - auto &info = c->buffer->info; - unsigned count = c->buffer->len; - for (unsigned i = 0; i < count; i++) - info[i].syllable() = 255; - c->new_syllables = 255; - return (void *) true; - } - case hb_ot_lookup_cache_op_t::LEAVE: - { - hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p; - c->new_syllables = (unsigned) -1; - HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable); - return nullptr; - } - case hb_ot_lookup_cache_op_t::DESTROY: - return nullptr; - } - return nullptr; + return context_cache_func (c, op); } - bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } - bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); } - bool _apply (hb_ot_apply_context_t *c, bool cached) const + struct external_cache_t + { + hb_ot_layout_binary_cache_t coverage; + }; + void *external_cache_create () const + { + external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t)); + if (likely (cache)) + { + cache->coverage.clear (); + } + return cache; + } + bool apply_cached (hb_ot_apply_context_t *c, void *external_cache) const { return _apply (c, true, external_cache); } + bool apply (hb_ot_apply_context_t *c, void *external_cache) const { return _apply (c, false, external_cache); } + bool _apply (hb_ot_apply_context_t *c, bool cached, void *external_cache) const { TRACE_APPLY (this); +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + external_cache_t *cache = (external_cache_t *) external_cache; + unsigned int index = (this+coverage).get_coverage_binary (c->buffer->cur().codepoint, cache ? &cache->coverage : nullptr); +#else unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); +#endif if (index == NOT_COVERED) return_trace (false); const ClassDef &class_def = this+classDef; @@ -2671,10 +2746,7 @@ struct ContextFormat2_5 &class_def }; - if (cached && c->buffer->cur().syllable() < 255) - index = c->buffer->cur().syllable (); - else - index = class_def.get_class (c->buffer->cur().codepoint); + index = cached ? get_class_cached (class_def, c->buffer->cur()) : class_def.get_class (c->buffer->cur().codepoint); const RuleSet &rule_set = this+ruleSet[index]; return_trace (rule_set.apply (c, lookup_context)); } @@ -2919,9 +2991,9 @@ struct Context 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) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward (ds)...)); case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward (ds)...)); @@ -2935,7 +3007,7 @@ struct Context protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ContextFormat1_4 format1; ContextFormat2_5 format2; ContextFormat3 format3; @@ -3069,27 +3141,18 @@ static inline bool chain_context_would_apply_lookup (hb_would_apply_context_t *c } template -HB_ALWAYS_INLINE -static bool chain_context_apply_lookup (hb_ot_apply_context_t *c, - unsigned int backtrackCount, - const HBUINT backtrack[], - unsigned int inputCount, /* Including the first glyph (not matched) */ - const HBUINT input[], /* Array of input values--start with second glyph */ - unsigned int lookaheadCount, - const HBUINT lookahead[], - unsigned int lookupCount, - const LookupRecord lookupRecord[], - const ChainContextApplyLookupContext &lookup_context) +static inline bool chain_context_apply_lookup (hb_ot_apply_context_t *c, + unsigned int backtrackCount, + const HBUINT backtrack[], + unsigned int inputCount, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + unsigned int lookaheadCount, + const HBUINT lookahead[], + unsigned int lookupCount, + const LookupRecord lookupRecord[], + const ChainContextApplyLookupContext &lookup_context) { if (unlikely (inputCount > HB_MAX_CONTEXT_LENGTH)) return false; - unsigned match_positions_stack[4]; - unsigned *match_positions = match_positions_stack; - if (unlikely (inputCount > ARRAY_LENGTH (match_positions_stack))) - { - match_positions = (unsigned *) hb_malloc (hb_max (inputCount, 1u) * sizeof (match_positions[0])); - if (unlikely (!match_positions)) - return false; - } unsigned start_index = c->buffer->out_len; unsigned end_index = c->buffer->idx; @@ -3098,15 +3161,14 @@ static bool chain_context_apply_lookup (hb_ot_apply_context_t *c, if (!(match_input (c, inputCount, input, lookup_context.funcs.match[1], lookup_context.match_data[1], - &match_end, match_positions) && (end_index = match_end) + &match_end) && (end_index = match_end) && match_lookahead (c, lookaheadCount, lookahead, lookup_context.funcs.match[2], lookup_context.match_data[2], match_end, &end_index))) { c->buffer->unsafe_to_concat (c->buffer->idx, end_index); - ret = false; - goto done; + return false; } if (!match_backtrack (c, @@ -3115,19 +3177,14 @@ static bool chain_context_apply_lookup (hb_ot_apply_context_t *c, &start_index)) { c->buffer->unsafe_to_concat_from_outbuffer (start_index, end_index); - ret = false; - goto done; + return false; } c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index); apply_lookup (c, - inputCount, match_positions, + inputCount, lookupCount, lookupRecord, match_end); - done: - - if (unlikely (match_positions != match_positions_stack)) - hb_free (match_positions); return ret; } @@ -3411,33 +3468,29 @@ struct ChainRuleSet * * Replicated from LigatureSet::apply(). */ - /* If the input skippy has non-auto joiners behavior (as in Indic shapers), - * skip this fast path, as we don't distinguish between input & lookahead - * matching in the fast path. + /* We use the iter_context instead of iter_input, to avoid skipping + * default-ignorables and such. * - * https://github.com/harfbuzz/harfbuzz/issues/4813 + * Related: https://github.com/harfbuzz/harfbuzz/issues/4813 */ - if (!c->auto_zwnj || !c->auto_zwj) - goto slow; - - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_context; skippy_iter.reset (c->buffer->idx); skippy_iter.set_match_func (match_always, nullptr); skippy_iter.set_glyph_data ((HBUINT16 *) nullptr); - unsigned unsafe_to = (unsigned) -1, unsafe_to1 = 0, unsafe_to2 = 0; + unsigned unsafe_to = (unsigned) -1, unsafe_to1, unsafe_to2 = 0; hb_glyph_info_t *first = nullptr, *second = nullptr; bool matched = skippy_iter.next (); if (likely (matched)) { - first = &c->buffer->info[skippy_iter.idx]; - unsafe_to1 = skippy_iter.idx + 1; - if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx])) { /* Can't use the fast path if eg. the next char is a default-ignorable * or other skippable. */ goto slow; } + + first = &c->buffer->info[skippy_iter.idx]; + unsafe_to1 = skippy_iter.idx + 1; } else { @@ -3458,8 +3511,15 @@ struct ChainRuleSet ; } matched = skippy_iter.next (); - if (likely (matched && !skippy_iter.may_skip (c->buffer->info[skippy_iter.idx]))) + if (likely (matched)) { + if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx])) + { + /* Can't use the fast path if eg. the next char is a default-ignorable + * or other skippable. */ + goto slow; + } + second = &c->buffer->info[skippy_iter.idx]; unsafe_to2 = skippy_iter.idx + 1; } @@ -3475,7 +3535,7 @@ struct ChainRuleSet const auto &input = StructAfter (r.backtrack); const auto &lookahead = StructAfter (input); - unsigned lenP1 = hb_max ((unsigned) input.lenP1, 1u); + unsigned lenP1 = input.lenP1; if (lenP1 > 1 ? (!match_input || match_input (*first, input.arrayZ[0], input_data)) @@ -3483,6 +3543,7 @@ struct ChainRuleSet (!lookahead.len || !match_lookahead || match_lookahead (*first, lookahead.arrayZ[0], lookahead_data))) { + lenP1 = hb_max (lenP1, 1u); if (!second || (lenP1 > 2 ? (!match_input || @@ -3505,6 +3566,18 @@ struct ChainRuleSet { if (unsafe_to == (unsigned) -1) unsafe_to = unsafe_to1; + + if (lenP1 > 1) + { + // Skip ahead to next possible first glyph match. + for (; i + 1 < num_rules; i++) + { + const auto &r2 = this+rule.arrayZ[i + 1]; + const auto &input2 = StructAfter (r2.backtrack); + if (input2.lenP1 <= 1 || input2.arrayZ[0] != input.arrayZ[0]) + break; + } + } } } if (likely (unsafe_to != (unsigned) -1)) @@ -3873,45 +3946,37 @@ struct ChainContextFormat2_5 unsigned cache_cost () const { - return (this+lookaheadClassDef).cost () * ruleSet.len; + return (this+inputClassDef).cost () + (this+lookaheadClassDef).cost (); } - static void * cache_func (void *p, hb_ot_lookup_cache_op_t op) + static bool cache_func (hb_ot_apply_context_t *c, hb_ot_subtable_cache_op_t op) { - switch (op) - { - case hb_ot_lookup_cache_op_t::CREATE: - return (void *) true; - case hb_ot_lookup_cache_op_t::ENTER: - { - hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p; - if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable)) - return (void *) false; - auto &info = c->buffer->info; - unsigned count = c->buffer->len; - for (unsigned i = 0; i < count; i++) - info[i].syllable() = 255; - c->new_syllables = 255; - return (void *) true; - } - case hb_ot_lookup_cache_op_t::LEAVE: - { - hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p; - c->new_syllables = (unsigned) -1; - HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable); - return nullptr; - } - case hb_ot_lookup_cache_op_t::DESTROY: - return nullptr; - } - return nullptr; + return context_cache_func (c, op); } - bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } - bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); } - bool _apply (hb_ot_apply_context_t *c, bool cached) const + struct external_cache_t + { + hb_ot_layout_binary_cache_t coverage; + }; + void *external_cache_create () const + { + external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t)); + if (likely (cache)) + { + cache->coverage.clear (); + } + return cache; + } + bool apply_cached (hb_ot_apply_context_t *c, void *external_cache) const { return _apply (c, true, external_cache); } + bool apply (hb_ot_apply_context_t *c, void *external_cache) const { return _apply (c, false, external_cache); } + bool _apply (hb_ot_apply_context_t *c, bool cached, void *external_cache) const { TRACE_APPLY (this); +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + external_cache_t *cache = (external_cache_t *) external_cache; + unsigned int index = (this+coverage).get_coverage_binary (c->buffer->cur().codepoint, cache ? &cache->coverage : nullptr); +#else unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); +#endif if (index == NOT_COVERED) return_trace (false); const ClassDef &backtrack_class_def = this+backtrackClassDef; @@ -3929,11 +3994,9 @@ struct ChainContextFormat2_5 &lookahead_class_def} }; - // Note: Corresponds to match_class_cached2 - if (cached && ((c->buffer->cur().syllable() & 0xF0) >> 4) < 15) - index = (c->buffer->cur().syllable () & 0xF0) >> 4; - else - index = input_class_def.get_class (c->buffer->cur().codepoint); + index = cached + ? get_class_cached2 (input_class_def, c->buffer->cur()) + : input_class_def.get_class (c->buffer->cur().codepoint); const ChainRuleSet &rule_set = this+ruleSet[index]; return_trace (rule_set.apply (c, lookup_context)); } @@ -4261,9 +4324,9 @@ struct ChainContext 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) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward (ds)...)); case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward (ds)...)); @@ -4277,7 +4340,7 @@ struct ChainContext protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ChainContextFormat1_4 format1; ChainContextFormat2_5 format2; ChainContextFormat3 format3; @@ -4352,7 +4415,7 @@ struct Extension { unsigned int get_type () const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_type (); default:return 0; } @@ -4360,7 +4423,7 @@ struct Extension template const X& get_subtable () const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.template get_subtable (); default:return Null (typename T::SubTable); } @@ -4372,7 +4435,7 @@ struct Extension template typename hb_subset_context_t::return_t dispatch (hb_subset_context_t *c, Ts&&... ds) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.subset (c); default: return c->default_return_value (); } @@ -4381,9 +4444,9 @@ struct Extension 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) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.dispatch (c, std::forward (ds)...)); default:return_trace (c->default_return_value ()); } @@ -4391,7 +4454,7 @@ struct Extension protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ExtensionFormat1 format1; } u; }; @@ -4428,22 +4491,14 @@ struct hb_ot_layout_lookup_accelerator_t for (auto& subtable : hb_iter (thiz->subtables, count)) thiz->digest.union_ (subtable.digest); + thiz->count = count; + #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - if (c_accelerate_subtables.cache_user_cost < 4) - c_accelerate_subtables.cache_user_idx = (unsigned) -1; - - thiz->cache_user_idx = c_accelerate_subtables.cache_user_idx; - - if (thiz->cache_user_idx != (unsigned) -1) - { - thiz->cache = thiz->subtables[thiz->cache_user_idx].cache_func (nullptr, hb_ot_lookup_cache_op_t::CREATE); - if (!thiz->cache) - thiz->cache_user_idx = (unsigned) -1; - } + thiz->subtable_cache_user_idx = c_accelerate_subtables.subtable_cache_user_idx; for (unsigned i = 0; i < count; i++) - if (i != thiz->cache_user_idx) - thiz->subtables[i].apply_cached_func = thiz->subtables[i].apply_func; + if (i != thiz->subtable_cache_user_idx) + thiz->subtables[i].apply_cached_func = thiz->subtables[i].apply_func; #endif return thiz; @@ -4452,11 +4507,8 @@ struct hb_ot_layout_lookup_accelerator_t void fini () { #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - if (cache) - { - assert (cache_user_idx != (unsigned) -1); - subtables[cache_user_idx].cache_func (cache, hb_ot_lookup_cache_op_t::DESTROY); - } + for (unsigned i = 0; i < count; i++) + hb_free (subtables[i].external_cache); #endif } @@ -4466,14 +4518,14 @@ struct hb_ot_layout_lookup_accelerator_t #ifndef HB_OPTIMIZE_SIZE HB_ALWAYS_INLINE #endif - bool apply (hb_ot_apply_context_t *c, unsigned subtables_count, bool use_cache) const + bool apply (hb_ot_apply_context_t *c, bool use_cache) const { c->lookup_accel = this; #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE if (use_cache) { return - + hb_iter (hb_iter (subtables, subtables_count)) + + hb_iter (hb_iter (subtables, count)) | hb_map ([&c] (const hb_accelerate_subtables_context_t::hb_applicable_t &_) { return _.apply_cached (c); }) | hb_any ; @@ -4482,7 +4534,7 @@ struct hb_ot_layout_lookup_accelerator_t #endif { return - + hb_iter (hb_iter (subtables, subtables_count)) + + hb_iter (hb_iter (subtables, count)) | hb_map ([&c] (const hb_accelerate_subtables_context_t::hb_applicable_t &_) { return _.apply (c); }) | hb_any ; @@ -4493,8 +4545,8 @@ struct hb_ot_layout_lookup_accelerator_t bool cache_enter (hb_ot_apply_context_t *c) const { #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - return cache_user_idx != (unsigned) -1 && - subtables[cache_user_idx].cache_enter (c); + return subtable_cache_user_idx != (unsigned) -1 && + subtables[subtable_cache_user_idx].cache_enter (c); #else return false; #endif @@ -4502,19 +4554,17 @@ struct hb_ot_layout_lookup_accelerator_t void cache_leave (hb_ot_apply_context_t *c) const { #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - subtables[cache_user_idx].cache_leave (c); + subtables[subtable_cache_user_idx].cache_leave (c); #endif } hb_set_digest_t digest; + private: + unsigned count = 0; /* Number of subtables in the array. */ #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - public: - void *cache = nullptr; - private: - unsigned cache_user_idx = (unsigned) -1; + unsigned subtable_cache_user_idx = (unsigned) -1; #endif - private: hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY]; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc index afe52963201..c73e4dc4b6d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc @@ -343,7 +343,7 @@ hb_ot_layout_get_glyphs_in_class (hb_face_t *face, * @face: The #hb_face_t to work on * @glyph: The #hb_codepoint_t code point to query * @start_offset: offset of the first attachment point to retrieve - * @point_count: (inout) (optional): Input = the maximum number of attachment points to return; + * @point_count: (inout) (nullable): Input = the maximum number of attachment points to return; * Output = the actual number of attachment points returned (may be zero) * @point_array: (out) (array length=point_count): The array of attachment points found for the query * @@ -373,7 +373,7 @@ hb_ot_layout_get_attach_points (hb_face_t *face, * @direction: The #hb_direction_t text direction to use * @glyph: The #hb_codepoint_t code point to query * @start_offset: offset of the first caret position to retrieve - * @caret_count: (inout) (optional): Input = the maximum number of caret positions to return; + * @caret_count: (inout) (nullable): Input = the maximum number of caret positions to return; * Output = the actual number of caret positions returned (may be zero) * @caret_array: (out) (array length=caret_count): The array of caret positions found for the query * @@ -444,7 +444,7 @@ get_gsubgpos_table (hb_face_t *face, * @face: #hb_face_t to work upon * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS * @start_offset: offset of the first script tag to retrieve - * @script_count: (inout) (optional): Input = the maximum number of script tags to return; + * @script_count: (inout) (nullable): Input = the maximum number of script tags to return; * Output = the actual number of script tags returned (may be zero) * @script_tags: (out) (array length=script_count): The array of #hb_tag_t script tags found for the query * @@ -541,8 +541,8 @@ hb_ot_layout_table_choose_script (hb_face_t *face, * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS * @script_count: Number of script tags in the array * @script_tags: Array of #hb_tag_t script tags - * @script_index: (out) (optional): The index of the requested script - * @chosen_script: (out) (optional): #hb_tag_t of the requested script + * @script_index: (out) (nullable): The index of the requested script + * @chosen_script: (out) (nullable): #hb_tag_t of the requested script * * Selects an OpenType script for @table_tag from the @script_tags array. * @@ -613,7 +613,7 @@ hb_ot_layout_table_select_script (hb_face_t *face, * @face: #hb_face_t to work upon * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS * @start_offset: offset of the first feature tag to retrieve - * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return; + * @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return; * Output = the actual number of feature tags returned (may be zero) * @feature_tags: (out) (array length=feature_count): Array of feature tags found in the table * @@ -683,7 +683,7 @@ hb_ot_layout_table_find_feature (hb_face_t *face, * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS * @script_index: The index of the requested script tag * @start_offset: offset of the first language tag to retrieve - * @language_count: (inout) (optional): Input = the maximum number of language tags to return; + * @language_count: (inout) (nullable): Input = the maximum number of language tags to return; * Output = the actual number of language tags returned (may be zero) * @language_tags: (out) (array length=language_count): Array of language tags found in the table * @@ -911,7 +911,7 @@ hb_ot_layout_language_get_required_feature (hb_face_t *face, * @script_index: The index of the requested script tag * @language_index: The index of the requested language tag * @start_offset: offset of the first feature tag to retrieve - * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return; + * @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return; * Output: the actual number of feature tags returned (may be zero) * @feature_indexes: (out) (array length=feature_count): The array of feature indexes found for the query * @@ -947,7 +947,7 @@ hb_ot_layout_language_get_feature_indexes (hb_face_t *face, * @script_index: The index of the requested script tag * @language_index: The index of the requested language tag * @start_offset: offset of the first feature tag to retrieve - * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return; + * @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return; * Output = the actual number of feature tags returned (may be zero) * @feature_tags: (out) (array length=feature_count): The array of #hb_tag_t feature tags found for the query * @@ -1035,7 +1035,7 @@ hb_ot_layout_language_find_feature (hb_face_t *face, * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS * @feature_index: The index of the requested feature * @start_offset: offset of the first lookup to retrieve - * @lookup_count: (inout) (optional): Input = the maximum number of lookups to return; + * @lookup_count: (inout) (nullable): Input = the maximum number of lookups to return; * Output = the actual number of lookups returned (may be zero) * @lookup_indexes: (out) (array length=lookup_count): The array of lookup indexes found for the query * @@ -1386,10 +1386,10 @@ hb_ot_layout_collect_lookups (hb_face_t *face, * @face: #hb_face_t to work upon * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS * @lookup_index: The index of the feature lookup to query - * @glyphs_before: (out): Array of glyphs preceding the substitution range - * @glyphs_input: (out): Array of input glyphs that would be substituted by the lookup - * @glyphs_after: (out): Array of glyphs following the substitution range - * @glyphs_output: (out): Array of glyphs that would be the substituted output of the lookup + * @glyphs_before: (out) (nullable): Array of glyphs preceding the substitution range + * @glyphs_input: (out) (nullable): Array of input glyphs that would be substituted by the lookup + * @glyphs_after: (out) (nullable): Array of glyphs following the substitution range + * @glyphs_output: (out) (nullable): Array of glyphs that would be the substituted output of the lookup * * Fetches a list of all glyphs affected by the specified lookup in the * specified face's GSUB table or GPOS table. @@ -1473,7 +1473,7 @@ hb_ot_layout_table_find_feature_variations (hb_face_t *face, * @feature_index: The index of the feature to query * @variations_index: The index of the feature variation to query * @start_offset: offset of the first lookup to retrieve - * @lookup_count: (inout) (optional): Input = the maximum number of lookups to return; + * @lookup_count: (inout) (nullable): Input = the maximum number of lookups to return; * Output = the actual number of lookups returned (may be zero) * @lookup_indexes: (out) (array length=lookup_count): The array of lookups found for the query * @@ -1777,15 +1777,15 @@ hb_ot_layout_get_size_params (hb_face_t *face, * @face: #hb_face_t to work upon * @table_tag: table tag to query, "GSUB" or "GPOS". * @feature_index: index of feature to query. - * @label_id: (out) (optional): The ‘name’ table name ID that specifies a string - * for a user-interface label for this feature. (May be NULL.) - * @tooltip_id: (out) (optional): The ‘name’ table name ID that specifies a string + * @label_id: (out) (nullable): The ‘name’ table name ID that specifies a string + * for a user-interface label for this feature. + * @tooltip_id: (out) (nullable): The ‘name’ table name ID that specifies a string * that an application can use for tooltip text for this - * feature. (May be NULL.) - * @sample_id: (out) (optional): The ‘name’ table name ID that specifies sample text - * that illustrates the effect of this feature. (May be NULL.) - * @num_named_parameters: (out) (optional): Number of named parameters. (May be zero.) - * @first_param_id: (out) (optional): The first ‘name’ table name ID used to specify + * feature. + * @sample_id: (out) (nullable): The ‘name’ table name ID that specifies sample text + * that illustrates the effect of this feature. + * @num_named_parameters: (out) (nullable): Number of named parameters. + * @first_param_id: (out) (nullable): The first ‘name’ table name ID used to specify * strings for user-interface labels for the feature * parameters. (Must be zero if numParameters is zero.) * @@ -1852,7 +1852,7 @@ hb_ot_layout_feature_get_name_ids (hb_face_t *face, * @table_tag: table tag to query, "GSUB" or "GPOS". * @feature_index: index of feature to query. * @start_offset: offset of the first character to retrieve - * @char_count: (inout) (optional): Input = the maximum number of characters to return; + * @char_count: (inout) (nullable): Input = the maximum number of characters to return; * Output = the actual number of characters returned (may be zero) * @characters: (out caller-allocates) (array length=char_count): A buffer pointer. * The Unicode codepoints of the characters for which this feature provides @@ -1915,31 +1915,33 @@ struct GPOSProxy static inline bool apply_forward (OT::hb_ot_apply_context_t *c, - const OT::hb_ot_layout_lookup_accelerator_t &accel, - unsigned subtable_count) + const OT::hb_ot_layout_lookup_accelerator_t &accel) { - bool use_cache = accel.cache_enter (c); + bool use_hot_subtable_cache = accel.cache_enter (c); bool ret = false; hb_buffer_t *buffer = c->buffer; - while (buffer->idx < buffer->len && buffer->successful) + while (buffer->successful) { - bool applied = false; - auto &cur = buffer->cur(); - if (accel.digest.may_have (cur.codepoint) && - (cur.mask & c->lookup_mask) && - c->check_glyph_property (&cur, c->lookup_props)) - { - applied = accel.apply (c, subtable_count, use_cache); - } + hb_glyph_info_t *info = buffer->info; + unsigned j = buffer->idx; + while (j < buffer->len && + !(accel.digest.may_have (info[j].codepoint) && + (info[j].mask & c->lookup_mask) && + c->check_glyph_property (&info[j], c->lookup_props))) + j++; + if (unlikely (j > buffer->idx && !buffer->next_glyphs (j - buffer->idx))) + break; + if (buffer->idx >= buffer->len) + break; - if (applied) + if (accel.apply (c, use_hot_subtable_cache)) ret = true; else (void) buffer->next_glyph (); } - if (use_cache) + if (use_hot_subtable_cache) accel.cache_leave (c); return ret; @@ -1947,8 +1949,7 @@ apply_forward (OT::hb_ot_apply_context_t *c, static inline bool apply_backward (OT::hb_ot_apply_context_t *c, - const OT::hb_ot_layout_lookup_accelerator_t &accel, - unsigned subtable_count) + const OT::hb_ot_layout_lookup_accelerator_t &accel) { bool ret = false; hb_buffer_t *buffer = c->buffer; @@ -1958,11 +1959,10 @@ apply_backward (OT::hb_ot_apply_context_t *c, if (accel.digest.may_have (cur.codepoint) && (cur.mask & c->lookup_mask) && c->check_glyph_property (&cur, c->lookup_props)) - ret |= accel.apply (c, subtable_count, false); + ret |= accel.apply (c, false); /* The reverse lookup doesn't "advance" cursor (for good reason). */ buffer->idx--; - } while ((int) buffer->idx >= 0); return ret; @@ -1975,7 +1975,6 @@ apply_string (OT::hb_ot_apply_context_t *c, const OT::hb_ot_layout_lookup_accelerator_t &accel) { hb_buffer_t *buffer = c->buffer; - unsigned subtable_count = lookup.get_subtable_count (); if (unlikely (!buffer->len || !c->lookup_mask)) return false; @@ -1991,7 +1990,7 @@ apply_string (OT::hb_ot_apply_context_t *c, buffer->clear_output (); buffer->idx = 0; - ret = apply_forward (c, accel, subtable_count); + ret = apply_forward (c, accel); if (!Proxy::always_inplace) buffer->sync (); @@ -2001,7 +2000,7 @@ apply_string (OT::hb_ot_apply_context_t *c, /* in-place backward substitution/positioning */ assert (!buffer->have_output); buffer->idx = buffer->len - 1; - ret = apply_backward (c, accel, subtable_count); + ret = apply_backward (c, accel); } return ret; @@ -2017,7 +2016,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, unsigned int i = 0; auto *font_data = font->data.ot.get (); - auto *var_store_cache = font_data == HB_SHAPER_DATA_SUCCEEDED ? nullptr : (OT::ItemVariationStore::cache_t *) font_data; + auto *var_store_cache = (OT::hb_scalar_cache_t *) font_data; OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob (), var_store_cache); c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func); @@ -2037,11 +2036,8 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, if (buffer->messaging () && !buffer->message (font, "start lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag))) continue; - /* c.digest is a digest of all the current glyphs in the buffer - * (plus some past glyphs). - * - * Only try applying the lookup if there is any overlap. */ - if (accel->digest.may_intersect (c.digest)) + /* Only try applying the lookup if there is any overlap. */ + if (accel->digest.may_intersect (buffer->digest)) { c.set_lookup_index (lookup_index); c.set_lookup_mask (lookup.mask, false); @@ -2067,7 +2063,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, if (stage->pause_func (plan, font, buffer)) { /* Refresh working buffer digest since buffer changed. */ - buffer->collect_codepoints (c.digest); + buffer->update_digest (); } } } @@ -2601,6 +2597,7 @@ hb_ot_layout_get_baseline_with_fallback2 (hb_font_t *font, #endif +#ifndef HB_NO_LAYOUT_RARELY_USED struct hb_get_glyph_alternates_dispatch_t : hb_dispatch_context_t { @@ -2620,14 +2617,13 @@ struct hb_get_glyph_alternates_dispatch_t : ( _dispatch (obj, hb_prioritize, std::forward (ds)...) ) }; -#ifndef HB_NO_LAYOUT_RARELY_USED /** * hb_ot_layout_lookup_get_glyph_alternates: * @face: a face. * @lookup_index: index of the feature lookup to query. * @glyph: a glyph id. * @start_offset: starting offset. - * @alternate_count: (inout) (optional): Input = the maximum number of alternate glyphs to return; + * @alternate_count: (inout) (nullable): Input = the maximum number of alternate glyphs to return; * Output = the actual number of alternate glyphs returned (may be zero). * @alternate_glyphs: (out caller-allocates) (array length=alternate_count): A glyphs buffer. * Alternate glyphs associated with the glyph id. @@ -2654,6 +2650,64 @@ hb_ot_layout_lookup_get_glyph_alternates (hb_face_t *face, return ret; } +struct hb_collect_glyph_alternates_dispatch_t : + hb_dispatch_context_t +{ + static return_t default_return_value () { return false; } + bool stop_sublookup_iteration (return_t r) const { return false; } + + private: + template auto + _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN + ( (obj.collect_glyph_alternates (std::forward (ds)...), true) ) + template auto + _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN + ( default_return_value () ) + public: + template auto + dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN + ( _dispatch (obj, hb_prioritize, std::forward (ds)...) ) +}; + +/** + * hb_ot_layout_lookup_collect_glyph_alternates: + * @face: a face. + * @lookup_index: index of the feature lookup to query. + * @alternate_count: (inout): mapping from glyph index to number of alternates for that glyph. + * @alternate_glyphs: (inout): mapping from encoded glyph index and alternate index, to alternate glyph ids. + * + * Collects alternates of glyphs from a given GSUB lookup index. + * + * For one-to-one GSUB glyph substitutions, this function collects the + * substituted glyph. + * + * For lookups that assign multiple alternates to a glyph, all alternate glyphs are collected. + * + * For other lookup types, nothing is performed and `false` is returned. + * + * The `alternate_count` mapping will contain the number of alternates for each glyph id. + * Upon entry, this mapping should contain the glyph ids as keys, and the number of alternates + * currently known for each glyph id as values. + * + * The `alternate_glyphs` mapping will contain the alternate glyph ids for each glyph id. + * The mapping is encoded in the following way, upon entry and after processing: + * If G is the glyph id, and A0, A1, ..., A(n-1) are the alternate glyph ids, + * the mapping will contain the following entries: (G + (i << 24)) -> A(i) + * for i = 0, 1, ..., n-1 where n is the number of alternates for G as per `alternate_count`. + * + * Return value: `true` if alternates were collected, `false` otherwise. + * Since: 12.1.0 + */ +HB_EXTERN hb_bool_t +hb_ot_layout_lookup_collect_glyph_alternates (hb_face_t *face, + unsigned lookup_index, + hb_map_t *alternate_count /* IN/OUT */, + hb_map_t *alternate_glyphs /* IN/OUT */) +{ + hb_collect_glyph_alternates_dispatch_t c; + const OT::SubstLookup &lookup = face->table.GSUB->table->get_lookup (lookup_index); + return lookup.dispatch (&c, alternate_count, alternate_glyphs); +} struct hb_position_single_dispatch_t : hb_dispatch_context_t diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h index 1d9db1105bd..b814c2acda3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h @@ -383,6 +383,12 @@ hb_ot_layout_lookup_get_glyph_alternates (hb_face_t *face, unsigned *alternate_count /* IN/OUT */, hb_codepoint_t *alternate_glyphs /* OUT */); +HB_EXTERN hb_bool_t +hb_ot_layout_lookup_collect_glyph_alternates (hb_face_t *face, + unsigned lookup_index, + hb_map_t *alternate_count /* IN/OUT */, + hb_map_t *alternate_glyphs /* IN/OUT */); + HB_EXTERN hb_bool_t hb_ot_layout_lookup_would_substitute (hb_face_t *face, unsigned int lookup_index, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh index a68c8421e4c..f669ac7c46d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh @@ -217,8 +217,6 @@ _hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_buffer_t *buffer) if (u >= 0x80u) { - buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII; - if (unlikely (unicode->is_default_ignorable (u))) { buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES; @@ -247,6 +245,7 @@ _hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_buffer_t *buffer) if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (gen_cat))) { + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_CONTINUATIONS; props |= UPROPS_MASK_CONTINUATION; props |= unicode->modified_combining_class (u)<<8; } @@ -361,8 +360,9 @@ _hb_glyph_info_unhide (hb_glyph_info_t *info) } static inline void -_hb_glyph_info_set_continuation (hb_glyph_info_t *info) +_hb_glyph_info_set_continuation (hb_glyph_info_t *info, hb_buffer_t *buffer) { + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_CONTINUATIONS; info->unicode_props() |= UPROPS_MASK_CONTINUATION; } static inline void @@ -410,18 +410,6 @@ _hb_glyph_info_is_zwj (const hb_glyph_info_t *info) return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & UPROPS_MASK_Cf_ZWJ); } static inline bool -_hb_glyph_info_is_joiner (const hb_glyph_info_t *info) -{ - return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & (UPROPS_MASK_Cf_ZWNJ|UPROPS_MASK_Cf_ZWJ)); -} -static inline void -_hb_glyph_info_flip_joiners (hb_glyph_info_t *info) -{ - if (!_hb_glyph_info_is_unicode_format (info)) - return; - info->unicode_props() ^= UPROPS_MASK_Cf_ZWNJ | UPROPS_MASK_Cf_ZWJ; -} -static inline bool _hb_glyph_info_is_aat_deleted (const hb_glyph_info_t *info) { return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & UPROPS_MASK_Cf_AAT_DELETED); @@ -657,4 +645,18 @@ _hb_buffer_assert_gsubgpos_vars (hb_buffer_t *buffer) #undef lig_props #undef glyph_props +static inline void +_hb_collect_glyph_alternates_add (hb_codepoint_t from, + hb_codepoint_t to, + hb_map_t *alternate_count, + hb_map_t *alternate_glyphs) +{ + hb_codepoint_t zero = 0; + hb_codepoint_t *i = &zero; + alternate_count->has (from, &i); + alternate_glyphs->set (from | (*i << 24), to); + alternate_count->set (from, *i + 1); +} + + #endif /* HB_OT_LAYOUT_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh index b33e01fd6c9..ad9d7cb03e8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh @@ -69,6 +69,8 @@ struct MathValueRecord struct MathConstants { + friend struct MATH; + MathConstants* copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); @@ -1109,8 +1111,8 @@ struct MATH { #ifndef HB_NO_MATH switch HB_CODEPOINT_ENCODE3 (font->face->table.MATH.get_blob ()->length, - get_constant (HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT, font), - get_constant (HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT, font)) + (this+mathConstants).minHeight[1], // displayOperatorMinHeight + (this+mathConstants).minHeight[0]) // delimitedSubFormulaMinHeight { /* sha1sum:ab4a4fe054d23061f3c039493d6f665cfda2ecf5 cambria.ttc * sha1sum:086855301bff644f9d8827b88491fcf73a6d4cb9 cambria.ttc diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-post-macroman.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-post-macroman.hh index b4df8aaeeab..269b4d3fe45 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-post-macroman.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-post-macroman.hh @@ -31,264 +31,264 @@ #endif -_S(".notdef") -_S(".null") -_S("nonmarkingreturn") -_S("space") -_S("exclam") -_S("quotedbl") -_S("numbersign") -_S("dollar") -_S("percent") -_S("ampersand") -_S("quotesingle") -_S("parenleft") -_S("parenright") -_S("asterisk") -_S("plus") -_S("comma") -_S("hyphen") -_S("period") -_S("slash") -_S("zero") -_S("one") -_S("two") -_S("three") -_S("four") -_S("five") -_S("six") -_S("seven") -_S("eight") -_S("nine") -_S("colon") -_S("semicolon") -_S("less") -_S("equal") -_S("greater") -_S("question") -_S("at") -_S("A") -_S("B") -_S("C") -_S("D") -_S("E") -_S("F") -_S("G") -_S("H") -_S("I") -_S("J") -_S("K") -_S("L") -_S("M") -_S("N") -_S("O") -_S("P") -_S("Q") -_S("R") -_S("S") -_S("T") -_S("U") -_S("V") -_S("W") -_S("X") -_S("Y") -_S("Z") -_S("bracketleft") -_S("backslash") -_S("bracketright") -_S("asciicircum") -_S("underscore") -_S("grave") -_S("a") -_S("b") -_S("c") -_S("d") -_S("e") -_S("f") -_S("g") -_S("h") -_S("i") -_S("j") -_S("k") -_S("l") -_S("m") -_S("n") -_S("o") -_S("p") -_S("q") -_S("r") -_S("s") -_S("t") -_S("u") -_S("v") -_S("w") -_S("x") -_S("y") -_S("z") -_S("braceleft") -_S("bar") -_S("braceright") -_S("asciitilde") -_S("Adieresis") -_S("Aring") -_S("Ccedilla") -_S("Eacute") -_S("Ntilde") -_S("Odieresis") -_S("Udieresis") -_S("aacute") -_S("agrave") -_S("acircumflex") -_S("adieresis") -_S("atilde") -_S("aring") -_S("ccedilla") -_S("eacute") -_S("egrave") -_S("ecircumflex") -_S("edieresis") -_S("iacute") -_S("igrave") -_S("icircumflex") -_S("idieresis") -_S("ntilde") -_S("oacute") -_S("ograve") -_S("ocircumflex") -_S("odieresis") -_S("otilde") -_S("uacute") -_S("ugrave") -_S("ucircumflex") -_S("udieresis") -_S("dagger") -_S("degree") -_S("cent") -_S("sterling") -_S("section") -_S("bullet") -_S("paragraph") -_S("germandbls") -_S("registered") -_S("copyright") -_S("trademark") -_S("acute") -_S("dieresis") -_S("notequal") -_S("AE") -_S("Oslash") -_S("infinity") -_S("plusminus") -_S("lessequal") -_S("greaterequal") -_S("yen") -_S("mu") -_S("partialdiff") -_S("summation") -_S("product") -_S("pi") -_S("integral") -_S("ordfeminine") -_S("ordmasculine") -_S("Omega") -_S("ae") -_S("oslash") -_S("questiondown") -_S("exclamdown") -_S("logicalnot") -_S("radical") -_S("florin") -_S("approxequal") -_S("Delta") -_S("guillemotleft") -_S("guillemotright") -_S("ellipsis") -_S("nonbreakingspace") -_S("Agrave") -_S("Atilde") -_S("Otilde") -_S("OE") -_S("oe") -_S("endash") -_S("emdash") -_S("quotedblleft") -_S("quotedblright") -_S("quoteleft") -_S("quoteright") -_S("divide") -_S("lozenge") -_S("ydieresis") -_S("Ydieresis") -_S("fraction") -_S("currency") -_S("guilsinglleft") -_S("guilsinglright") -_S("fi") -_S("fl") -_S("daggerdbl") -_S("periodcentered") -_S("quotesinglbase") -_S("quotedblbase") -_S("perthousand") -_S("Acircumflex") -_S("Ecircumflex") -_S("Aacute") -_S("Edieresis") -_S("Egrave") -_S("Iacute") -_S("Icircumflex") -_S("Idieresis") -_S("Igrave") -_S("Oacute") -_S("Ocircumflex") -_S("apple") -_S("Ograve") -_S("Uacute") -_S("Ucircumflex") -_S("Ugrave") -_S("dotlessi") -_S("circumflex") -_S("tilde") -_S("macron") -_S("breve") -_S("dotaccent") -_S("ring") -_S("cedilla") -_S("hungarumlaut") -_S("ogonek") -_S("caron") -_S("Lslash") -_S("lslash") -_S("Scaron") -_S("scaron") -_S("Zcaron") -_S("zcaron") -_S("brokenbar") -_S("Eth") -_S("eth") -_S("Yacute") -_S("yacute") -_S("Thorn") -_S("thorn") -_S("minus") -_S("multiply") -_S("onesuperior") -_S("twosuperior") -_S("threesuperior") -_S("onehalf") -_S("onequarter") -_S("threequarters") -_S("franc") -_S("Gbreve") -_S("gbreve") -_S("Idotaccent") -_S("Scedilla") -_S("scedilla") -_S("Cacute") -_S("cacute") -_S("Ccaron") -_S("ccaron") -_S("dcroat") +HB_STR(".notdef") +HB_STR(".null") +HB_STR("nonmarkingreturn") +HB_STR("space") +HB_STR("exclam") +HB_STR("quotedbl") +HB_STR("numbersign") +HB_STR("dollar") +HB_STR("percent") +HB_STR("ampersand") +HB_STR("quotesingle") +HB_STR("parenleft") +HB_STR("parenright") +HB_STR("asterisk") +HB_STR("plus") +HB_STR("comma") +HB_STR("hyphen") +HB_STR("period") +HB_STR("slash") +HB_STR("zero") +HB_STR("one") +HB_STR("two") +HB_STR("three") +HB_STR("four") +HB_STR("five") +HB_STR("six") +HB_STR("seven") +HB_STR("eight") +HB_STR("nine") +HB_STR("colon") +HB_STR("semicolon") +HB_STR("less") +HB_STR("equal") +HB_STR("greater") +HB_STR("question") +HB_STR("at") +HB_STR("A") +HB_STR("B") +HB_STR("C") +HB_STR("D") +HB_STR("E") +HB_STR("F") +HB_STR("G") +HB_STR("H") +HB_STR("I") +HB_STR("J") +HB_STR("K") +HB_STR("L") +HB_STR("M") +HB_STR("N") +HB_STR("O") +HB_STR("P") +HB_STR("Q") +HB_STR("R") +HB_STR("S") +HB_STR("T") +HB_STR("U") +HB_STR("V") +HB_STR("W") +HB_STR("X") +HB_STR("Y") +HB_STR("Z") +HB_STR("bracketleft") +HB_STR("backslash") +HB_STR("bracketright") +HB_STR("asciicircum") +HB_STR("underscore") +HB_STR("grave") +HB_STR("a") +HB_STR("b") +HB_STR("c") +HB_STR("d") +HB_STR("e") +HB_STR("f") +HB_STR("g") +HB_STR("h") +HB_STR("i") +HB_STR("j") +HB_STR("k") +HB_STR("l") +HB_STR("m") +HB_STR("n") +HB_STR("o") +HB_STR("p") +HB_STR("q") +HB_STR("r") +HB_STR("s") +HB_STR("t") +HB_STR("u") +HB_STR("v") +HB_STR("w") +HB_STR("x") +HB_STR("y") +HB_STR("z") +HB_STR("braceleft") +HB_STR("bar") +HB_STR("braceright") +HB_STR("asciitilde") +HB_STR("Adieresis") +HB_STR("Aring") +HB_STR("Ccedilla") +HB_STR("Eacute") +HB_STR("Ntilde") +HB_STR("Odieresis") +HB_STR("Udieresis") +HB_STR("aacute") +HB_STR("agrave") +HB_STR("acircumflex") +HB_STR("adieresis") +HB_STR("atilde") +HB_STR("aring") +HB_STR("ccedilla") +HB_STR("eacute") +HB_STR("egrave") +HB_STR("ecircumflex") +HB_STR("edieresis") +HB_STR("iacute") +HB_STR("igrave") +HB_STR("icircumflex") +HB_STR("idieresis") +HB_STR("ntilde") +HB_STR("oacute") +HB_STR("ograve") +HB_STR("ocircumflex") +HB_STR("odieresis") +HB_STR("otilde") +HB_STR("uacute") +HB_STR("ugrave") +HB_STR("ucircumflex") +HB_STR("udieresis") +HB_STR("dagger") +HB_STR("degree") +HB_STR("cent") +HB_STR("sterling") +HB_STR("section") +HB_STR("bullet") +HB_STR("paragraph") +HB_STR("germandbls") +HB_STR("registered") +HB_STR("copyright") +HB_STR("trademark") +HB_STR("acute") +HB_STR("dieresis") +HB_STR("notequal") +HB_STR("AE") +HB_STR("Oslash") +HB_STR("infinity") +HB_STR("plusminus") +HB_STR("lessequal") +HB_STR("greaterequal") +HB_STR("yen") +HB_STR("mu") +HB_STR("partialdiff") +HB_STR("summation") +HB_STR("product") +HB_STR("pi") +HB_STR("integral") +HB_STR("ordfeminine") +HB_STR("ordmasculine") +HB_STR("Omega") +HB_STR("ae") +HB_STR("oslash") +HB_STR("questiondown") +HB_STR("exclamdown") +HB_STR("logicalnot") +HB_STR("radical") +HB_STR("florin") +HB_STR("approxequal") +HB_STR("Delta") +HB_STR("guillemotleft") +HB_STR("guillemotright") +HB_STR("ellipsis") +HB_STR("nonbreakingspace") +HB_STR("Agrave") +HB_STR("Atilde") +HB_STR("Otilde") +HB_STR("OE") +HB_STR("oe") +HB_STR("endash") +HB_STR("emdash") +HB_STR("quotedblleft") +HB_STR("quotedblright") +HB_STR("quoteleft") +HB_STR("quoteright") +HB_STR("divide") +HB_STR("lozenge") +HB_STR("ydieresis") +HB_STR("Ydieresis") +HB_STR("fraction") +HB_STR("currency") +HB_STR("guilsinglleft") +HB_STR("guilsinglright") +HB_STR("fi") +HB_STR("fl") +HB_STR("daggerdbl") +HB_STR("periodcentered") +HB_STR("quotesinglbase") +HB_STR("quotedblbase") +HB_STR("perthousand") +HB_STR("Acircumflex") +HB_STR("Ecircumflex") +HB_STR("Aacute") +HB_STR("Edieresis") +HB_STR("Egrave") +HB_STR("Iacute") +HB_STR("Icircumflex") +HB_STR("Idieresis") +HB_STR("Igrave") +HB_STR("Oacute") +HB_STR("Ocircumflex") +HB_STR("apple") +HB_STR("Ograve") +HB_STR("Uacute") +HB_STR("Ucircumflex") +HB_STR("Ugrave") +HB_STR("dotlessi") +HB_STR("circumflex") +HB_STR("tilde") +HB_STR("macron") +HB_STR("breve") +HB_STR("dotaccent") +HB_STR("ring") +HB_STR("cedilla") +HB_STR("hungarumlaut") +HB_STR("ogonek") +HB_STR("caron") +HB_STR("Lslash") +HB_STR("lslash") +HB_STR("Scaron") +HB_STR("scaron") +HB_STR("Zcaron") +HB_STR("zcaron") +HB_STR("brokenbar") +HB_STR("Eth") +HB_STR("eth") +HB_STR("Yacute") +HB_STR("yacute") +HB_STR("Thorn") +HB_STR("thorn") +HB_STR("minus") +HB_STR("multiply") +HB_STR("onesuperior") +HB_STR("twosuperior") +HB_STR("threesuperior") +HB_STR("onehalf") +HB_STR("onequarter") +HB_STR("threequarters") +HB_STR("franc") +HB_STR("Gbreve") +HB_STR("gbreve") +HB_STR("Idotaccent") +HB_STR("Scedilla") +HB_STR("scedilla") +HB_STR("Cacute") +HB_STR("cacute") +HB_STR("Ccaron") +HB_STR("ccaron") +HB_STR("dcroat") #endif /* HB_OT_POST_MACROMAN_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc index 2401e18f0e6..c17e7628975 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc @@ -409,16 +409,13 @@ position_around_base (const hb_ot_shape_plan_t *plan, } static inline void -position_cluster (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer, - unsigned int start, - unsigned int end, - bool adjust_offsets_when_zeroing) +position_cluster_impl (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + bool adjust_offsets_when_zeroing) { - if (end - start < 2) - return; - /* Find the base glyph */ hb_glyph_info_t *info = buffer->info; for (unsigned int i = start; i < end; i++) @@ -441,6 +438,20 @@ position_cluster (const hb_ot_shape_plan_t *plan, } } +static HB_ALWAYS_INLINE void +position_cluster (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + bool adjust_offsets_when_zeroing) +{ + if (end - start < 2) + return; + + position_cluster_impl (plan, font, buffer, start, end, adjust_offsets_when_zeroing); +} + void _hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan, hb_font_t *font, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc index cb941bc7fbd..508ed6a5076 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc @@ -78,14 +78,14 @@ static inline void set_glyph (hb_glyph_info_t &info, hb_font_t *font) { - (void) font->get_nominal_glyph (info.codepoint, &info.glyph_index()); + (void) font->get_nominal_glyph (info.codepoint, &info.normalizer_glyph_index()); } static inline void output_char (hb_buffer_t *buffer, hb_codepoint_t unichar, hb_codepoint_t glyph) { /* This is very confusing indeed. */ - buffer->cur().glyph_index() = glyph; + buffer->cur().normalizer_glyph_index() = glyph; (void) buffer->output_glyph (unichar); _hb_glyph_info_set_unicode_props (&buffer->prev(), buffer); } @@ -93,7 +93,7 @@ output_char (hb_buffer_t *buffer, hb_codepoint_t unichar, hb_codepoint_t glyph) static inline void next_char (hb_buffer_t *buffer, hb_codepoint_t glyph) { - buffer->cur().glyph_index() = glyph; + buffer->cur().normalizer_glyph_index() = glyph; (void) buffer->next_glyph (); } @@ -210,7 +210,7 @@ handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, hb_font_t * const font = c->font; for (; buffer->idx < end - 1 && buffer->successful;) { if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepoint))) { - if (font->get_variation_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().glyph_index())) + if (font->get_variation_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().normalizer_glyph_index())) { hb_codepoint_t unicode = buffer->cur().codepoint; (void) buffer->replace_glyphs (2, 1, &unicode); @@ -342,7 +342,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, unsigned int done = font->get_nominal_glyphs (end - buffer->idx, &buffer->cur().codepoint, sizeof (buffer->info[0]), - &buffer->cur().glyph_index(), + &buffer->cur().normalizer_glyph_index(), sizeof (buffer->info[0])); if (unlikely (!buffer->next_glyphs (done))) break; } @@ -456,7 +456,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, buffer->out_len--; /* Remove the second composable. */ /* Modify starter and carry on. */ buffer->out_info[starter].codepoint = composed; - buffer->out_info[starter].glyph_index() = glyph; + buffer->out_info[starter].normalizer_glyph_index() = glyph; _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer); continue; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.hh index 9f17bdbb243..138e895e8e1 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.hh @@ -32,7 +32,7 @@ /* buffer var allocations, used during the normalization process */ -#define glyph_index() var1.u32 +#define normalizer_glyph_index() var1.u32 struct hb_ot_shape_plan_t; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc index 477a7c7fc72..69b188f7aa3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc @@ -44,6 +44,7 @@ #include "hb-ot-face.hh" #include "hb-set.hh" +#include "hb-unicode.hh" #include "hb-aat-layout.hh" #include "hb-ot-layout-gdef-table.hh" @@ -92,7 +93,7 @@ hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t *fac shaper = hb_ot_shaper_categorize (props.script, props.direction, map.chosen_script[0]); script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE; - script_fallback_mark_positioning = shaper->fallback_position; + script_fallback_position = shaper->fallback_position; #ifndef HB_NO_AAT_SHAPE /* https://github.com/harfbuzz/harfbuzz/issues/1528 */ @@ -178,12 +179,12 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, #endif #ifndef HB_NO_OT_KERN else if (hb_ot_layout_has_kerning (face)) - plan.apply_kern = true; + plan.apply_kern = script_fallback_position; // Not all shapers apply legacy `kern` #endif else {} } - plan.apply_fallback_kern = !(plan.apply_gpos || plan.apply_kerx || plan.apply_kern); + plan.apply_fallback_kern = script_fallback_position && !(plan.apply_gpos || plan.apply_kerx || plan.apply_kern); plan.zero_marks = script_zero_marks && !plan.apply_kerx && @@ -203,7 +204,7 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, ); plan.fallback_mark_positioning = plan.adjust_mark_positioning_when_zeroing && - script_fallback_mark_positioning; + script_fallback_position; #ifndef HB_NO_AAT_SHAPE /* If we're using morx shaping, we cancel mark position adjustment because @@ -425,25 +426,20 @@ _hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data) */ struct hb_ot_font_data_t { - OT::ItemVariationStore::cache_t unused; // Just for alignment + OT::hb_scalar_cache_t unused; // Just for alignment }; hb_ot_font_data_t * _hb_ot_shaper_font_data_create (hb_font_t *font) { - if (!font->num_coords) - return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; - const OT::ItemVariationStore &var_store = font->face->table.GDEF->table->get_var_store (); - auto *cache = (hb_ot_font_data_t *) var_store.create_cache (); - return cache ? cache : (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; + return (hb_ot_font_data_t *) var_store.create_cache (); } void _hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data) { - if (data == HB_SHAPER_DATA_SUCCEEDED) return; - OT::ItemVariationStore::destroy_cache ((OT::ItemVariationStore::cache_t *) data); + OT::ItemVariationStore::destroy_cache ((OT::hb_scalar_cache_t *) data); } @@ -488,6 +484,9 @@ hb_set_unicode_props (hb_buffer_t *buffer) { _hb_glyph_info_set_unicode_props (&info[i], buffer); + if (info[i].codepoint < 0x80) + continue; + unsigned gen_cat = _hb_glyph_info_get_general_category (&info[i]); if (FLAG_UNSAFE (gen_cat) & (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) | @@ -502,7 +501,7 @@ hb_set_unicode_props (hb_buffer_t *buffer) if (unlikely (gen_cat == HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL && hb_in_range (info[i].codepoint, 0x1F3FBu, 0x1F3FFu))) { - _hb_glyph_info_set_continuation (&info[i]); + _hb_glyph_info_set_continuation (&info[i], buffer); } /* Regional_Indicators are hairy as hell... * https://github.com/harfbuzz/harfbuzz/issues/2265 */ @@ -510,18 +509,18 @@ hb_set_unicode_props (hb_buffer_t *buffer) { if (_hb_codepoint_is_regional_indicator (info[i - 1].codepoint) && !_hb_glyph_info_is_continuation (&info[i - 1])) - _hb_glyph_info_set_continuation (&info[i]); + _hb_glyph_info_set_continuation (&info[i], buffer); } #ifndef HB_NO_EMOJI_SEQUENCES else if (unlikely (_hb_glyph_info_is_zwj (&info[i]))) { - _hb_glyph_info_set_continuation (&info[i]); + _hb_glyph_info_set_continuation (&info[i], buffer); if (i + 1 < count && _hb_unicode_is_emoji_Extended_Pictographic (info[i + 1].codepoint)) { i++; _hb_glyph_info_set_unicode_props (&info[i], buffer); - _hb_glyph_info_set_continuation (&info[i]); + _hb_glyph_info_set_continuation (&info[i], buffer); } } #endif @@ -540,7 +539,9 @@ hb_set_unicode_props (hb_buffer_t *buffer) * https://github.com/harfbuzz/harfbuzz/issues/3844 */ else if (unlikely (hb_in_ranges (info[i].codepoint, 0xFF9Eu, 0xFF9Fu, 0xE0020u, 0xE007Fu))) - _hb_glyph_info_set_continuation (&info[i]); + _hb_glyph_info_set_continuation (&info[i], buffer); + else if (unlikely (info[i].codepoint == 0x2044u /* FRACTION SLASH */)) + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_FRACTION_SLASH; } } @@ -576,7 +577,7 @@ hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) static void hb_form_clusters (hb_buffer_t *buffer) { - if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII)) + if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CONTINUATIONS)) return; if (HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES (buffer->cluster_level)) @@ -618,14 +619,14 @@ hb_ensure_native_direction (hb_buffer_t *buffer) for (unsigned i = 0; i < count; i++) { auto gc = _hb_glyph_info_get_general_category (&info[i]); - if (gc == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) - found_number = true; - else if (HB_UNICODE_GENERAL_CATEGORY_IS_LETTER (gc)) + if (HB_UNICODE_GENERAL_CATEGORY_IS_LETTER (gc)) { found_letter = true; break; } - else if (_hb_codepoint_is_regional_indicator (info[i].codepoint)) + else if (gc == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) + found_number = true; + else if (unlikely (_hb_codepoint_is_regional_indicator (info[i].codepoint))) found_ri = true; } if ((found_number || found_ri) && !found_letter) @@ -651,59 +652,6 @@ hb_ensure_native_direction (hb_buffer_t *buffer) * Substitute */ -#ifndef HB_NO_VERTICAL -static hb_codepoint_t -hb_vert_char_for (hb_codepoint_t u) -{ - switch (u >> 8) - { - case 0x20: switch (u) { - case 0x2013u: return 0xfe32u; // EN DASH - case 0x2014u: return 0xfe31u; // EM DASH - case 0x2025u: return 0xfe30u; // TWO DOT LEADER - case 0x2026u: return 0xfe19u; // HORIZONTAL ELLIPSIS - } break; - case 0x30: switch (u) { - case 0x3001u: return 0xfe11u; // IDEOGRAPHIC COMMA - case 0x3002u: return 0xfe12u; // IDEOGRAPHIC FULL STOP - case 0x3008u: return 0xfe3fu; // LEFT ANGLE BRACKET - case 0x3009u: return 0xfe40u; // RIGHT ANGLE BRACKET - case 0x300au: return 0xfe3du; // LEFT DOUBLE ANGLE BRACKET - case 0x300bu: return 0xfe3eu; // RIGHT DOUBLE ANGLE BRACKET - case 0x300cu: return 0xfe41u; // LEFT CORNER BRACKET - case 0x300du: return 0xfe42u; // RIGHT CORNER BRACKET - case 0x300eu: return 0xfe43u; // LEFT WHITE CORNER BRACKET - case 0x300fu: return 0xfe44u; // RIGHT WHITE CORNER BRACKET - case 0x3010u: return 0xfe3bu; // LEFT BLACK LENTICULAR BRACKET - case 0x3011u: return 0xfe3cu; // RIGHT BLACK LENTICULAR BRACKET - case 0x3014u: return 0xfe39u; // LEFT TORTOISE SHELL BRACKET - case 0x3015u: return 0xfe3au; // RIGHT TORTOISE SHELL BRACKET - case 0x3016u: return 0xfe17u; // LEFT WHITE LENTICULAR BRACKET - case 0x3017u: return 0xfe18u; // RIGHT WHITE LENTICULAR BRACKET - } break; - case 0xfe: switch (u) { - case 0xfe4fu: return 0xfe34u; // WAVY LOW LINE - } break; - case 0xff: switch (u) { - case 0xff01u: return 0xfe15u; // FULLWIDTH EXCLAMATION MARK - case 0xff08u: return 0xfe35u; // FULLWIDTH LEFT PARENTHESIS - case 0xff09u: return 0xfe36u; // FULLWIDTH RIGHT PARENTHESIS - case 0xff0cu: return 0xfe10u; // FULLWIDTH COMMA - case 0xff1au: return 0xfe13u; // FULLWIDTH COLON - case 0xff1bu: return 0xfe14u; // FULLWIDTH SEMICOLON - case 0xff1fu: return 0xfe16u; // FULLWIDTH QUESTION MARK - case 0xff3bu: return 0xfe47u; // FULLWIDTH LEFT SQUARE BRACKET - case 0xff3du: return 0xfe48u; // FULLWIDTH RIGHT SQUARE BRACKET - case 0xff3fu: return 0xfe33u; // FULLWIDTH LOW LINE - case 0xff5bu: return 0xfe37u; // FULLWIDTH LEFT CURLY BRACKET - case 0xff5du: return 0xfe38u; // FULLWIDTH RIGHT CURLY BRACKET - } break; - } - - return u; -} -#endif - static inline void hb_ot_rotate_chars (const hb_ot_shape_context_t *c) { @@ -729,7 +677,7 @@ hb_ot_rotate_chars (const hb_ot_shape_context_t *c) if (HB_DIRECTION_IS_VERTICAL (c->target_direction) && !c->plan->has_vert) { for (unsigned int i = 0; i < count; i++) { - hb_codepoint_t codepoint = hb_vert_char_for (info[i].codepoint); + hb_codepoint_t codepoint = hb_unicode_funcs_t::vertical_char_for (info[i].codepoint); if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint))) info[i].codepoint = codepoint; } @@ -744,7 +692,7 @@ hb_ot_shape_setup_masks_fraction (const hb_ot_shape_context_t *c) return; #endif - if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) || + if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_FRACTION_SLASH) || !c->plan->has_frac) return; @@ -845,7 +793,13 @@ hb_ot_zero_width_default_ignorables (const hb_buffer_t *buffer) unsigned int i = 0; for (i = 0; i < count; i++) if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i]))) - pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; + { + pos[i].x_advance = pos[i].y_advance = 0; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + pos[i].x_offset = 0; + else + pos[i].y_offset = 0; + } } static void @@ -900,11 +854,11 @@ hb_ot_hide_default_ignorables (hb_buffer_t *buffer, static inline void hb_ot_map_glyphs_fast (hb_buffer_t *buffer) { - /* Normalization process sets up glyph_index(), we just copy it. */ + /* Normalization process sets up normalizer_glyph_index(), we just copy it. */ unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) - info[i].codepoint = info[i].glyph_index(); + info[i].codepoint = info[i].normalizer_glyph_index(); buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; } @@ -942,7 +896,7 @@ hb_ot_substitute_default (const hb_ot_shape_context_t *c) hb_ot_rotate_chars (c); - HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index); + HB_BUFFER_ALLOCATE_VAR (buffer, normalizer_glyph_index); _hb_ot_shape_normalize (c->plan, buffer, c->font); @@ -954,7 +908,7 @@ hb_ot_substitute_default (const hb_ot_shape_context_t *c) hb_ot_map_glyphs_fast (buffer); - HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_index); + HB_BUFFER_DEALLOCATE_VAR (buffer, normalizer_glyph_index); } static inline void @@ -969,11 +923,17 @@ hb_ot_substitute_plan (const hb_ot_shape_context_t *c) #ifndef HB_NO_AAT_SHAPE if (unlikely (c->plan->apply_morx)) + { hb_aat_layout_substitute (c->plan, c->font, c->buffer, c->user_features, c->num_user_features); + c->buffer->update_digest (); + } else #endif + { + c->buffer->update_digest (); c->plan->substitute (c->font, buffer); + } } static inline void @@ -1054,23 +1014,16 @@ hb_ot_position_default (const hb_ot_shape_context_t *c) { c->font->get_glyph_h_advances (count, &info[0].codepoint, sizeof(info[0]), &pos[0].x_advance, sizeof(pos[0])); - /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ - if (c->font->has_glyph_h_origin_func ()) - for (unsigned int i = 0; i < count; i++) - c->font->subtract_glyph_h_origin (info[i].codepoint, - &pos[i].x_offset, - &pos[i].y_offset); + // h_origin defaults to zero; only apply it if the font has it. + if (c->font->has_glyph_h_origin_func () || c->font->has_glyph_h_origins_func ()) + c->font->subtract_glyph_h_origins (c->buffer); } else { c->font->get_glyph_v_advances (count, &info[0].codepoint, sizeof(info[0]), &pos[0].y_advance, sizeof(pos[0])); - for (unsigned int i = 0; i < count; i++) - { - c->font->subtract_glyph_v_origin (info[i].codepoint, - &pos[i].x_offset, - &pos[i].y_offset); - } + // v_origin defaults to non-zero; apply even if only fallback is there. + c->font->subtract_glyph_v_origins (c->buffer); } if (c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK) _hb_ot_shape_fallback_spaces (c->plan, c->font, c->buffer); @@ -1079,10 +1032,6 @@ hb_ot_position_default (const hb_ot_shape_context_t *c) static inline void hb_ot_position_plan (const hb_ot_shape_context_t *c) { - unsigned int count = c->buffer->len; - hb_glyph_info_t *info = c->buffer->info; - hb_glyph_position_t *pos = c->buffer->pos; - /* If the font has no GPOS and direction is forward, then when * zeroing mark widths, we shift the mark with it, such that the * mark is positioned hanging over the previous glyph. When @@ -1097,12 +1046,9 @@ hb_ot_position_plan (const hb_ot_shape_context_t *c) /* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */ - /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ - if (c->font->has_glyph_h_origin_func ()) - for (unsigned int i = 0; i < count; i++) - c->font->add_glyph_h_origin (info[i].codepoint, - &pos[i].x_offset, - &pos[i].y_offset); + // h_origin defaults to zero; only apply it if the font has it. + if (c->font->has_glyph_h_origin_func () || c->font->has_glyph_h_origins_func ()) + c->font->add_glyph_h_origins (c->buffer); hb_ot_layout_position_start (c->font, c->buffer); @@ -1139,12 +1085,9 @@ hb_ot_position_plan (const hb_ot_shape_context_t *c) hb_ot_zero_width_default_ignorables (c->buffer); hb_ot_layout_position_finish_offsets (c->font, c->buffer); - /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ - if (c->font->has_glyph_h_origin_func ()) - for (unsigned int i = 0; i < count; i++) - c->font->subtract_glyph_h_origin (info[i].codepoint, - &pos[i].x_offset, - &pos[i].y_offset); + // h_origin defaults to zero; only apply it if the font has it. + if (c->font->has_glyph_h_origin_func () || c->font->has_glyph_h_origins_func ()) + c->font->subtract_glyph_h_origins (c->buffer); if (c->plan->fallback_mark_positioning) _hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer, @@ -1172,8 +1115,33 @@ hb_propagate_flags (hb_buffer_t *buffer) /* Propagate cluster-level glyph flags to be the same on all cluster glyphs. * Simplifies using them. */ - if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS)) + hb_mask_t and_mask = HB_GLYPH_FLAG_DEFINED; + if ((buffer->flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0) + and_mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; + + hb_glyph_info_t *info = buffer->info; + + if ((buffer->flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL) == 0) + { + foreach_cluster (buffer, start, end) + { + if (end - start == 1) + { + info[start].mask &= and_mask; + continue; + } + + unsigned int mask = 0; + for (unsigned int i = start; i < end; i++) + mask |= info[i].mask; + + mask &= and_mask; + + for (unsigned int i = start; i < end; i++) + info[i].mask = mask; + } return; + } /* If we are producing SAFE_TO_INSERT_TATWEEL, then do two things: * @@ -1181,30 +1149,20 @@ hb_propagate_flags (hb_buffer_t *buffer) * are UNSAFE_TO_BREAK, then clear the SAFE_TO_INSERT_TATWEEL, * - Any place that is SAFE_TO_INSERT_TATWEEL, is also now UNSAFE_TO_BREAK. * - * We couldn't make this interaction earlier. It has to be done here. + * We couldn't make this interaction earlier. It has to be done this way. */ - bool flip_tatweel = buffer->flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL; - - bool clear_concat = (buffer->flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0; - - hb_glyph_info_t *info = buffer->info; - foreach_cluster (buffer, start, end) { unsigned int mask = 0; for (unsigned int i = start; i < end; i++) - mask |= info[i].mask & HB_GLYPH_FLAG_DEFINED; + mask |= info[i].mask; - if (flip_tatweel) - { - if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) - mask &= ~HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL; - if (mask & HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL) - mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; - } + if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) + mask &= ~HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL; + if (mask & HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL) + mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; - if (clear_concat) - mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; + mask &= and_mask; for (unsigned int i = start; i < end; i++) info[i].mask = mask; @@ -1245,8 +1203,6 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c) _hb_buffer_deallocate_unicode_vars (c->buffer); c->buffer->props.direction = c->target_direction; - - c->buffer->leave (); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh index 068d7192d0d..649acddb414 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh @@ -150,7 +150,7 @@ struct hb_ot_shape_planner_t static constexpr bool apply_morx = false; #endif bool script_zero_marks : 1; - bool script_fallback_mark_positioning : 1; + bool script_fallback_position : 1; const struct hb_ot_shaper_t *shaper; HB_INTERNAL hb_ot_shape_planner_t (hb_face_t *face, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-joining-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-joining-list.hh index e38686e3ebb..79d3014d3e1 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-joining-list.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-joining-list.hh @@ -6,10 +6,10 @@ * * on files with these headers: * - * # ArabicShaping-16.0.0.txt - * # Date: 2024-07-30 - * # Scripts-16.0.0.txt - * # Date: 2024-04-30, 21:48:40 GMT + * # ArabicShaping-17.0.0.txt + * # Date: 2025-08-14 + * # Scripts-17.0.0.txt + * # Date: 2025-07-24, 13:28:55 GMT */ #ifndef HB_OT_SHAPER_ARABIC_JOINING_LIST_HH diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-table.hh index 19bd72d4218..5f87995ea9c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-table.hh @@ -6,10 +6,10 @@ * * on files with these headers: * - * # ArabicShaping-16.0.0.txt - * # Date: 2024-07-30 - * # Blocks-16.0.0.txt - * # Date: 2024-02-02 + * # ArabicShaping-17.0.0.txt + * # Date: 2025-08-14 + * # Blocks-17.0.0.txt + * # Date: 2025-08-01 * UnicodeData.txt does not have a header. */ @@ -80,7 +80,7 @@ static const uint8_t joining_table[] = /* Arabic Extended-B */ /* 0860 */ R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R, - /* 0880 */ R,R,R,C,C,C,D,U,U,D,D,D,D,D,R,X,U,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 0880 */ R,R,R,C,C,C,D,U,U,D,D,D,D,D,R,D,U,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X, /* Arabic Extended-A */ @@ -140,9 +140,9 @@ static const uint8_t joining_table[] = /* Arabic Extended-C */ - /* 10EC0 */ R,D,D, + /* 10EC0 */ R,D,D,X,D,D, -#define joining_offset_0x10f30u 1185 +#define joining_offset_0x10f30u 1188 /* Sogdian */ @@ -161,14 +161,14 @@ static const uint8_t joining_table[] = /* 10FA0 */ D,U,D,D,R,R,R,U,D,R,R,D,D,R,D,D, /* 10FC0 */ U,D,R,R,D,U,U,U,U,R,D,L, -#define joining_offset_0x110bdu 1341 +#define joining_offset_0x110bdu 1344 /* Kaithi */ /* 110A0 */ U,X,X, /* 110C0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,U, -#define joining_offset_0x1e900u 1358 +#define joining_offset_0x1e900u 1361 /* Adlam */ @@ -176,7 +176,7 @@ static const uint8_t joining_table[] = /* 1E920 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, /* 1E940 */ D,D,D,D,X,X,X,X,X,X,X,T, -}; /* Table items: 1434; occupancy: 57% */ +}; /* Table items: 1437; occupancy: 58% */ static unsigned int @@ -204,7 +204,7 @@ joining_type (hb_codepoint_t u) if (hb_in_range (u, 0x10AC0u, 0x10AEFu)) return joining_table[u - 0x10AC0u + joining_offset_0x10ac0u]; if (hb_in_range (u, 0x10B80u, 0x10BAFu)) return joining_table[u - 0x10B80u + joining_offset_0x10b80u]; if (hb_in_range (u, 0x10D00u, 0x10D23u)) return joining_table[u - 0x10D00u + joining_offset_0x10d00u]; - if (hb_in_range (u, 0x10EC2u, 0x10EC4u)) return joining_table[u - 0x10EC2u + joining_offset_0x10ec2u]; + if (hb_in_range (u, 0x10EC2u, 0x10EC7u)) return joining_table[u - 0x10EC2u + joining_offset_0x10ec2u]; if (hb_in_range (u, 0x10F30u, 0x10FCBu)) return joining_table[u - 0x10F30u + joining_offset_0x10f30u]; break; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc index 7379bb7f15b..981c0f9224b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc @@ -654,7 +654,7 @@ postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan, /* https://www.unicode.org/reports/tr53/ */ -static hb_codepoint_t +static const hb_codepoint_t modifier_combining_marks[] = { 0x0654u, /* ARABIC HAMZA ABOVE */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc index 5c46ebbc281..5f15aff8b7c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc @@ -427,7 +427,7 @@ const hb_ot_shaper_t _hb_ot_shaper_hangul = HB_TAG_NONE, /* gpos_tag */ HB_OT_SHAPE_NORMALIZATION_MODE_NONE, HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, - false, /* fallback_position */ + true, /* fallback_position */ }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-machine.hh index 25e6d85ef80..76c5e83e30f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-machine.hh @@ -53,7 +53,7 @@ enum indic_syllable_type_t { }; -#line 57 "hb-ot-shaper-indic-machine.hh" +#line 54 "hb-ot-shaper-indic-machine.hh" #define indic_syllable_machine_ex_A 9u #define indic_syllable_machine_ex_C 1u #define indic_syllable_machine_ex_CM 16u @@ -77,7 +77,7 @@ enum indic_syllable_type_t { #define indic_syllable_machine_ex_ZWNJ 5u -#line 81 "hb-ot-shaper-indic-machine.hh" +#line 76 "hb-ot-shaper-indic-machine.hh" static const unsigned char _indic_syllable_machine_trans_keys[] = { 8u, 57u, 4u, 57u, 5u, 57u, 5u, 57u, 13u, 13u, 4u, 57u, 4u, 57u, 4u, 57u, 8u, 57u, 5u, 57u, 5u, 57u, 13u, 13u, 4u, 57u, 4u, 57u, 4u, 57u, 4u, 57u, @@ -1126,7 +1126,7 @@ find_syllables_indic (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 1130 "hb-ot-shaper-indic-machine.hh" +#line 1119 "hb-ot-shaper-indic-machine.hh" { cs = indic_syllable_machine_start; ts = 0; @@ -1142,7 +1142,7 @@ find_syllables_indic (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 1146 "hb-ot-shaper-indic-machine.hh" +#line 1131 "hb-ot-shaper-indic-machine.hh" { int _slen; int _trans; @@ -1156,7 +1156,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 1160 "hb-ot-shaper-indic-machine.hh" +#line 1143 "hb-ot-shaper-indic-machine.hh" } _keys = _indic_syllable_machine_trans_keys + (cs<<1); @@ -1268,7 +1268,7 @@ _eof_trans: #line 117 "hb-ot-shaper-indic-machine.rl" {act = 7;} break; -#line 1272 "hb-ot-shaper-indic-machine.hh" +#line 1232 "hb-ot-shaper-indic-machine.hh" } _again: @@ -1277,7 +1277,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 1281 "hb-ot-shaper-indic-machine.hh" +#line 1239 "hb-ot-shaper-indic-machine.hh" } if ( ++p != pe ) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-table.cc index b87c530853b..bf27efee21b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-table.cc @@ -6,12 +6,12 @@ * * on files with these headers: * - * # IndicSyllabicCategory-16.0.0.txt - * # Date: 2024-04-30, 21:48:21 GMT - * # IndicPositionalCategory-16.0.0.txt - * # Date: 2024-04-30, 21:48:21 GMT - * # Blocks-16.0.0.txt - * # Date: 2024-02-02 + * # IndicSyllabicCategory-17.0.0.txt + * # Date: 2025-08-01, 04:02:23 GMT + * # IndicPositionalCategory-17.0.0.txt + * # Date: 2025-07-29, 13:35:52 GMT + * # Blocks-17.0.0.txt + * # Date: 2025-08-01 */ #include "hb.hh" diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc index d78b5670f61..bec802429ab 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc @@ -296,11 +296,6 @@ struct indic_shape_plan_t const indic_config_t *config; bool is_old_spec; -#ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE - bool uniscribe_bug_compatible; -#else - static constexpr bool uniscribe_bug_compatible = false; -#endif mutable hb_atomic_t virama_glyph; hb_indic_would_substitute_feature_t rphf; @@ -327,9 +322,6 @@ data_create_indic (const hb_ot_shape_plan_t *plan) } indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FFu) != '2'); -#ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE - indic_plan->uniscribe_bug_compatible = hb_options ().uniscribe_bug_compatible; -#endif indic_plan->virama_glyph = -1; /* Use zero-context would_substitute() matching for new-spec of the main @@ -943,17 +935,7 @@ initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan, unsigned int start, unsigned int end) { /* We treat placeholder/dotted-circle as if they are consonants, so we - * should just chain. Only if not in compatibility mode that is... */ - - const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; - if (indic_plan->uniscribe_bug_compatible) - { - /* For dotted-circle, this is what Uniscribe does: - * If dotted-circle is the last glyph, it just does nothing. - * Ie. It doesn't form Reph. */ - if (buffer->info[end - 1].indic_category() == I_Cat(DOTTEDCIRCLE)) - return; - } + * should just chain... */ initial_reordering_consonant_syllable (plan, face, buffer, start, end); } @@ -1347,8 +1329,7 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, * Uniscribe doesn't do this. * TEST: U+0930,U+094D,U+0915,U+094B,U+094D */ - if (!indic_plan->uniscribe_bug_compatible && - unlikely (is_halant (info[new_reph_pos]))) + if (unlikely (is_halant (info[new_reph_pos]))) { for (unsigned int i = base + 1; i < new_reph_pos; i++) if (FLAG_UNSAFE (info[i].indic_category()) & (FLAG (I_Cat(M)) | FLAG (I_Cat(MPst)))) @@ -1451,27 +1432,6 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, else buffer->unsafe_to_break (start - 1, start + 1); } - - - /* - * Finish off the clusters and go home! - */ - if (indic_plan->uniscribe_bug_compatible) - { - switch ((hb_tag_t) plan->props.script) - { - case HB_SCRIPT_TAMIL: - break; - - default: - /* Uniscribe merges the entire syllable into a single cluster... Except for Tamil. - * This means, half forms are submerged into the main consonant's cluster. - * This is unnecessary, and makes cursor positioning harder, but that's what - * Uniscribe does. */ - buffer->merge_clusters (start, end); - break; - } - } } @@ -1501,9 +1461,7 @@ preprocess_text_indic (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer, hb_font_t *font) { - const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; - if (!indic_plan->uniscribe_bug_compatible) - _hb_preprocess_text_vowel_constraints (plan, buffer, font); + _hb_preprocess_text_vowel_constraints (plan, buffer, font); } static bool diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer-machine.hh index e1f657c758a..2442f0341ef 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer-machine.hh @@ -48,7 +48,7 @@ enum khmer_syllable_type_t { }; -#line 52 "hb-ot-shaper-khmer-machine.hh" +#line 49 "hb-ot-shaper-khmer-machine.hh" #define khmer_syllable_machine_ex_C 1u #define khmer_syllable_machine_ex_DOTTEDCIRCLE 11u #define khmer_syllable_machine_ex_H 4u @@ -66,7 +66,7 @@ enum khmer_syllable_type_t { #define khmer_syllable_machine_ex_ZWNJ 5u -#line 70 "hb-ot-shaper-khmer-machine.hh" +#line 65 "hb-ot-shaper-khmer-machine.hh" static const unsigned char _khmer_syllable_machine_trans_keys[] = { 5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, @@ -294,7 +294,7 @@ find_syllables_khmer (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 298 "hb-ot-shaper-khmer-machine.hh" +#line 287 "hb-ot-shaper-khmer-machine.hh" { cs = khmer_syllable_machine_start; ts = 0; @@ -310,7 +310,7 @@ find_syllables_khmer (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 314 "hb-ot-shaper-khmer-machine.hh" +#line 299 "hb-ot-shaper-khmer-machine.hh" { int _slen; int _trans; @@ -324,7 +324,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 328 "hb-ot-shaper-khmer-machine.hh" +#line 311 "hb-ot-shaper-khmer-machine.hh" } _keys = _khmer_syllable_machine_trans_keys + (cs<<1); @@ -394,7 +394,7 @@ _eof_trans: #line 98 "hb-ot-shaper-khmer-machine.rl" {act = 3;} break; -#line 398 "hb-ot-shaper-khmer-machine.hh" +#line 368 "hb-ot-shaper-khmer-machine.hh" } _again: @@ -403,7 +403,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 407 "hb-ot-shaper-khmer-machine.hh" +#line 375 "hb-ot-shaper-khmer-machine.hh" } if ( ++p != pe ) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer.cc index 2a4aed2ab1b..1f5028aa3ee 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer.cc @@ -141,12 +141,6 @@ override_features_khmer (hb_ot_shape_planner_t *plan) * typographical correctness.", hence in overrides... */ map->enable_feature (HB_TAG('c','l','i','g')); - /* Uniscribe does not apply 'kern' in Khmer. */ - if (hb_options ().uniscribe_bug_compatible) - { - map->disable_feature (HB_TAG('k','e','r','n')); - } - map->disable_feature (HB_TAG('l','i','g','a')); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-myanmar-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-myanmar-machine.hh index 64eb761b4ea..6e5fce609bc 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-myanmar-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-myanmar-machine.hh @@ -50,7 +50,7 @@ enum myanmar_syllable_type_t { }; -#line 54 "hb-ot-shaper-myanmar-machine.hh" +#line 51 "hb-ot-shaper-myanmar-machine.hh" #define myanmar_syllable_machine_ex_A 9u #define myanmar_syllable_machine_ex_As 32u #define myanmar_syllable_machine_ex_C 1u @@ -78,7 +78,7 @@ enum myanmar_syllable_type_t { #define myanmar_syllable_machine_ex_ZWNJ 5u -#line 82 "hb-ot-shaper-myanmar-machine.hh" +#line 77 "hb-ot-shaper-myanmar-machine.hh" static const unsigned char _myanmar_syllable_machine_trans_keys[] = { 1u, 57u, 3u, 57u, 5u, 57u, 5u, 57u, 3u, 57u, 5u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 5u, 57u, 1u, 15u, 3u, 57u, 3u, 57u, 3u, 57u, @@ -549,7 +549,7 @@ find_syllables_myanmar (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 553 "hb-ot-shaper-myanmar-machine.hh" +#line 542 "hb-ot-shaper-myanmar-machine.hh" { cs = myanmar_syllable_machine_start; ts = 0; @@ -565,7 +565,7 @@ find_syllables_myanmar (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 569 "hb-ot-shaper-myanmar-machine.hh" +#line 554 "hb-ot-shaper-myanmar-machine.hh" { int _slen; int _trans; @@ -579,7 +579,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 583 "hb-ot-shaper-myanmar-machine.hh" +#line 566 "hb-ot-shaper-myanmar-machine.hh" } _keys = _myanmar_syllable_machine_trans_keys + (cs<<1); @@ -649,7 +649,7 @@ _eof_trans: #line 113 "hb-ot-shaper-myanmar-machine.rl" {act = 3;} break; -#line 653 "hb-ot-shaper-myanmar-machine.hh" +#line 623 "hb-ot-shaper-myanmar-machine.hh" } _again: @@ -658,7 +658,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 662 "hb-ot-shaper-myanmar-machine.hh" +#line 630 "hb-ot-shaper-myanmar-machine.hh" } if ( ++p != pe ) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc index a0ea464e775..42fc4bbcc44 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc @@ -163,7 +163,7 @@ thai_pua_shape (hb_codepoint_t u, thai_action_t action, hb_font_t *font) } -static enum thai_above_state_t +static const enum thai_above_state_t { /* Cluster above looks like: */ T0, /* ⣤ */ T1, /* ⣼ */ @@ -191,7 +191,7 @@ static const struct thai_above_state_machine_edge_t { }; -static enum thai_below_state_t +static const enum thai_below_state_t { B0, /* No descender */ B1, /* Removable descender */ @@ -334,7 +334,7 @@ preprocess_text_thai (const hb_ot_shape_plan_t *plan, /* Is SARA AM. Decompose and reorder. */ (void) buffer->output_glyph (NIKHAHIT_FROM_SARA_AM (u)); - _hb_glyph_info_set_continuation (&buffer->prev()); + _hb_glyph_info_set_continuation (&buffer->prev(), buffer); if (unlikely (!buffer->replace_glyph (SARA_AA_FROM_SARA_AM (u)))) break; /* Make Nikhahit be recognized as a ccc=0 mark when zeroing widths. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-machine.hh index 46f66f7d285..22a5356a877 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-machine.hh @@ -53,7 +53,7 @@ enum use_syllable_type_t { }; -#line 57 "hb-ot-shaper-use-machine.hh" +#line 54 "hb-ot-shaper-use-machine.hh" #define use_syllable_machine_ex_B 1u #define use_syllable_machine_ex_CGJ 6u #define use_syllable_machine_ex_CMAbv 31u @@ -100,7 +100,7 @@ enum use_syllable_type_t { #define use_syllable_machine_ex_ZWNJ 14u -#line 104 "hb-ot-shaper-use-machine.hh" +#line 99 "hb-ot-shaper-use-machine.hh" static const unsigned char _use_syllable_machine_trans_keys[] = { 49u, 51u, 0u, 56u, 11u, 56u, 11u, 56u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u, 14u, 47u, 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u, @@ -929,7 +929,7 @@ find_syllables_use (hb_buffer_t *buffer) unsigned int act HB_UNUSED; int cs; -#line 933 "hb-ot-shaper-use-machine.hh" +#line 922 "hb-ot-shaper-use-machine.hh" { cs = use_syllable_machine_start; ts = 0; @@ -942,7 +942,7 @@ find_syllables_use (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 946 "hb-ot-shaper-use-machine.hh" +#line 931 "hb-ot-shaper-use-machine.hh" { int _slen; int _trans; @@ -956,7 +956,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 960 "hb-ot-shaper-use-machine.hh" +#line 943 "hb-ot-shaper-use-machine.hh" } _keys = _use_syllable_machine_trans_keys + (cs<<1); @@ -1078,7 +1078,7 @@ _eof_trans: #line 181 "hb-ot-shaper-use-machine.rl" {act = 9;} break; -#line 1082 "hb-ot-shaper-use-machine.hh" +#line 1039 "hb-ot-shaper-use-machine.hh" } _again: @@ -1087,7 +1087,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 1091 "hb-ot-shaper-use-machine.hh" +#line 1046 "hb-ot-shaper-use-machine.hh" } if ( ++p != pe ) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-table.hh index d3c49949aa8..a69abefdd01 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-table.hh @@ -6,18 +6,18 @@ * * on files with these headers: * - * # IndicSyllabicCategory-16.0.0.txt - * # Date: 2024-04-30, 21:48:21 GMT - * # IndicPositionalCategory-16.0.0.txt - * # Date: 2024-04-30, 21:48:21 GMT - * # ArabicShaping-16.0.0.txt - * # Date: 2024-07-30 - * # DerivedCoreProperties-16.0.0.txt - * # Date: 2024-05-31, 18:09:32 GMT - * # Blocks-16.0.0.txt - * # Date: 2024-02-02 - * # Scripts-16.0.0.txt - * # Date: 2024-04-30, 21:48:40 GMT + * # IndicSyllabicCategory-17.0.0.txt + * # Date: 2025-08-01, 04:02:23 GMT + * # IndicPositionalCategory-17.0.0.txt + * # Date: 2025-07-29, 13:35:52 GMT + * # ArabicShaping-17.0.0.txt + * # Date: 2025-08-14 + * # DerivedCoreProperties-17.0.0.txt + * # Date: 2025-07-30, 23:55:08 GMT + * # Blocks-17.0.0.txt + * # Date: 2025-08-01 + * # Scripts-17.0.0.txt + * # Date: 2025-07-24, 13:28:55 GMT * # Override values For Indic_Syllabic_Category * # Not derivable * # Initial version based on Unicode 7.0 by Andrew Glass 2014-03-17 @@ -101,8 +101,9 @@ #ifndef HB_OPTIMIZE_SIZE -static const uint8_t -hb_use_u8[3345] = +#include + +static const uint8_t hb_use_u8[3343]= { 16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 57, 58, 59, 195, 211, 62, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, @@ -126,24 +127,24 @@ hb_use_u8[3345] = 49, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 50, 51, 2, 2, 2, 2, 2, 2, 2, 2, 52, 53, 2, 54, 2, 2, 55, 56, 2, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 2, 70, 71, 72, 73, - 2, 74, 2, 75, 76, 77, 78, 2, 2, 79, 80, 81, 82, 2, 83, 84, - 2, 85, 85, 85, 85, 85, 85, 85, 85, 86, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 87, 2, 2, 2, 2, 2, 2, 2, + 2, 74, 2, 75, 76, 77, 78, 79, 2, 80, 81, 82, 83, 2, 84, 85, + 2, 86, 86, 86, 86, 86, 86, 86, 86, 87, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 88, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 88, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 89, 90, 2, 2, 2, 91, 2, 2, 2, 92, - 93, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 94, 94, 94, 95, 2, 2, 2, 2, 2, + 2, 2, 2, 89, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 90, 91, 2, 2, 2, 92, 2, 2, 2, 93, + 94, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 95, 95, 95, 96, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 96, 97, 2, 2, 2, 2, 2, - 2, 2, 2, 98, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 97, 98, 2, 2, 2, 2, 2, + 2, 2, 2, 99, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 99, 2, 2, 100, 2, 2, 2, 101, 2, 102, 2, 2, 2, - 2, 2, 2, 103, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 104, 104, 105, 106, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, - 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, - 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 2, 2, 2, 100, 2, 2, 101, 2, 2, 2, 102, 2, 103, 2, 2, 2, + 2, 2, 2, 104, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 105, 105, 106, 107, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0, 5, 0, 0, 0, 0, 0, 6, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -192,99 +193,99 @@ hb_use_u8[3345] = 48, 48, 48, 48, 15, 82, 83, 84, 85, 86, 87, 0, 0, 0, 0, 88, 0, 9, 0, 0, 30, 0, 89, 81, 90, 2, 2, 2, 2, 9, 0, 0, 0, 42, 42, 91, 92, 2, 2, 2, 2, 2, 2, 2, 2, 13, 9, 0, - 0, 93, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 9, 22, 80, 45, 22, 94, 61, 0, 0, 95, 96, 95, 95, 97, 98, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 9, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 29, 0, 0, - 0, 2, 2, 2, 2, 2, 9, 0, 0, 2, 2, 2, 52, 99, 45, 0, - 0, 2, 2, 100, 101, 102, 103, 61, 63, 104, 16, 45, 22, 59, 21, 80, - 48, 48, 76, 11, 11, 11, 105, 46, 40, 11, 106, 74, 2, 2, 2, 2, - 2, 2, 2, 107, 22, 20, 20, 22, 48, 48, 22, 108, 2, 2, 2, 9, - 0, 0, 0, 0, 0, 0, 109, 110, 110, 110, 110, 0, 0, 0, 0, 0, - 0, 106, 74, 2, 2, 2, 2, 2, 2, 60, 61, 59, 25, 22, 111, 61, - 2, 2, 2, 2, 107, 22, 23, 45, 45, 102, 112, 0, 0, 0, 0, 0, - 0, 2, 2, 61, 18, 48, 23, 113, 102, 102, 102, 114, 115, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 0, 30, 2, 11, 46, 116, 116, 116, 11, 116, - 116, 15, 116, 116, 116, 26, 0, 40, 0, 0, 0, 117, 51, 11, 5, 0, - 0, 0, 0, 0, 0, 0, 118, 0, 0, 0, 0, 0, 0, 0, 6, 119, - 120, 42, 42, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 120, - 121, 120, 120, 120, 120, 120, 120, 120, 120, 0, 0, 122, 0, 0, 0, 0, - 0, 0, 7, 122, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 123, 123, 0, 0, - 0, 2, 2, 2, 2, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, - 124, 0, 123, 123, 0, 0, 0, 0, 0, 2, 53, 2, 108, 2, 10, 2, - 2, 2, 65, 19, 16, 0, 0, 31, 0, 2, 2, 0, 0, 0, 0, 0, - 0, 29, 2, 2, 2, 2, 2, 2, 2, 2, 2, 125, 23, 23, 23, 23, - 23, 23, 23, 126, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 2, 0, 0, 0, 0, 0, 52, 2, 2, 2, 22, 22, 127, 116, - 0, 2, 2, 2, 128, 20, 59, 20, 113, 102, 129, 0, 0, 0, 0, 0, - 0, 11, 130, 2, 2, 2, 2, 2, 2, 2, 131, 23, 22, 20, 48, 132, - 133, 134, 0, 0, 0, 0, 0, 0, 0, 2, 2, 52, 30, 2, 2, 2, - 2, 2, 2, 2, 2, 10, 22, 59, 99, 76, 135, 136, 137, 0, 0, 0, - 0, 2, 138, 2, 2, 2, 2, 139, 0, 30, 2, 42, 5, 0, 79, 15, - 2, 53, 22, 140, 52, 53, 2, 2, 105, 10, 9, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 141, 21, 25, 0, 0, 142, 143, 0, 0, 0, - 0, 2, 65, 45, 23, 80, 47, 144, 0, 81, 81, 81, 81, 81, 81, 81, - 81, 0, 0, 0, 0, 0, 0, 0, 6, 120, 120, 120, 120, 121, 0, 0, - 0, 2, 2, 2, 2, 2, 9, 2, 2, 2, 9, 2, 30, 2, 2, 2, - 2, 2, 30, 2, 2, 2, 30, 9, 0, 128, 20, 27, 31, 0, 0, 145, - 146, 2, 2, 30, 2, 30, 2, 2, 2, 2, 2, 2, 0, 14, 37, 0, - 147, 2, 2, 13, 37, 0, 30, 2, 2, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 30, 2, 2, 9, 2, 2, 11, 41, 0, 0, 0, - 0, 2, 2, 2, 0, 27, 22, 22, 30, 2, 2, 2, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 27, 38, 0, 2, 2, 2, 116, 116, 116, 116, - 116, 148, 2, 9, 0, 0, 0, 0, 0, 2, 14, 14, 0, 0, 0, 0, - 0, 9, 2, 2, 9, 2, 2, 2, 2, 30, 2, 9, 0, 30, 2, 0, - 0, 149, 150, 151, 2, 2, 2, 2, 2, 2, 2, 2, 2, 22, 22, 20, - 20, 20, 22, 22, 134, 0, 0, 0, 0, 0, 152, 152, 152, 152, 152, 152, - 152, 152, 152, 152, 2, 2, 2, 2, 2, 53, 52, 53, 0, 0, 0, 0, - 153, 11, 74, 2, 2, 2, 2, 2, 2, 18, 19, 21, 16, 24, 37, 0, - 0, 0, 31, 0, 0, 0, 0, 0, 0, 11, 49, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 128, 20, 22, 154, 22, 21, 155, 156, 2, 2, 2, 2, - 2, 0, 0, 65, 157, 0, 0, 0, 0, 2, 13, 0, 0, 0, 0, 0, - 0, 2, 65, 25, 20, 20, 20, 22, 22, 108, 158, 0, 0, 56, 159, 31, - 160, 30, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 23, - 19, 22, 22, 161, 44, 0, 0, 0, 49, 128, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 9, 9, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, - 30, 2, 2, 2, 2, 2, 2, 2, 10, 18, 19, 21, 22, 162, 31, 0, - 0, 11, 11, 30, 2, 2, 2, 9, 30, 9, 2, 30, 2, 2, 58, 17, - 23, 16, 23, 47, 32, 33, 32, 34, 0, 0, 0, 0, 35, 0, 0, 0, - 2, 2, 23, 0, 11, 11, 11, 46, 0, 11, 11, 46, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 30, 0, 9, 2, 2, 2, 30, 45, 59, 20, - 20, 31, 33, 32, 32, 25, 163, 29, 164, 165, 37, 0, 0, 0, 0, 0, - 0, 12, 26, 0, 0, 0, 0, 0, 0, 2, 2, 65, 25, 20, 20, 20, - 22, 23, 126, 15, 17, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, - 166, 167, 0, 0, 0, 0, 0, 0, 0, 18, 19, 20, 20, 66, 99, 25, - 160, 11, 168, 9, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, - 65, 25, 20, 20, 0, 48, 48, 11, 169, 37, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2, 2, 20, 0, 23, 19, 20, 20, 21, 16, 82, - 169, 38, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 10, 170, - 25, 20, 22, 22, 168, 9, 0, 0, 0, 2, 2, 2, 2, 2, 9, 43, - 136, 23, 22, 20, 76, 21, 22, 0, 0, 2, 2, 2, 9, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 18, 19, 20, 21, 22, 105, 169, 37, 0, - 0, 2, 2, 2, 9, 30, 0, 2, 2, 2, 2, 30, 9, 2, 2, 2, - 2, 23, 23, 18, 32, 33, 12, 171, 165, 172, 173, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 0, 2, 2, 2, 65, 25, 20, 20, 0, 22, 23, - 29, 108, 0, 33, 0, 0, 0, 0, 0, 52, 20, 22, 22, 22, 140, 2, - 2, 2, 174, 175, 11, 15, 176, 61, 177, 0, 0, 1, 147, 0, 0, 0, - 0, 52, 20, 22, 16, 19, 20, 2, 2, 2, 2, 158, 158, 158, 178, 178, - 178, 178, 178, 178, 15, 179, 0, 30, 0, 22, 20, 20, 31, 22, 22, 11, + 0, 2, 2, 2, 2, 2, 2, 2, 9, 22, 80, 45, 22, 93, 61, 0, + 0, 94, 95, 94, 94, 96, 97, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 0, 2, 2, 9, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, + 0, 2, 2, 2, 2, 29, 0, 0, 0, 2, 2, 2, 2, 2, 9, 0, + 0, 2, 2, 2, 52, 98, 45, 0, 0, 2, 2, 99, 100, 101, 102, 61, + 63, 103, 16, 45, 22, 59, 21, 80, 48, 48, 76, 11, 11, 11, 104, 46, + 40, 11, 105, 74, 2, 2, 2, 2, 2, 2, 2, 106, 22, 20, 20, 22, + 48, 48, 22, 107, 2, 2, 2, 9, 0, 0, 0, 0, 0, 0, 108, 109, + 109, 109, 109, 0, 0, 0, 0, 0, 0, 105, 74, 2, 2, 2, 2, 2, + 2, 60, 61, 59, 25, 22, 110, 61, 2, 2, 2, 2, 106, 22, 23, 45, + 45, 101, 111, 0, 0, 0, 0, 0, 0, 2, 2, 61, 18, 48, 23, 112, + 101, 101, 101, 113, 114, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 30, + 2, 11, 46, 115, 115, 115, 11, 115, 115, 15, 115, 115, 115, 26, 0, 40, + 0, 0, 0, 116, 51, 11, 5, 0, 0, 0, 0, 0, 0, 0, 117, 0, + 0, 0, 0, 0, 0, 0, 6, 118, 119, 42, 42, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 119, 120, 119, 119, 119, 119, 119, 119, 119, + 119, 0, 0, 121, 0, 0, 0, 0, 0, 0, 7, 121, 0, 0, 0, 0, + 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 0, 0, 0, 0, 122, 122, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, + 30, 0, 0, 0, 0, 0, 0, 0, 123, 0, 122, 122, 0, 0, 0, 0, + 0, 2, 53, 2, 107, 2, 10, 2, 2, 2, 65, 19, 16, 0, 0, 31, + 0, 2, 2, 0, 0, 0, 0, 0, 0, 29, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 124, 23, 23, 23, 23, 23, 23, 23, 125, 0, 0, 0, 0, + 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 0, 0, 0, 0, 0, + 52, 2, 2, 2, 22, 22, 126, 115, 0, 2, 2, 2, 127, 20, 59, 20, + 112, 101, 128, 0, 0, 0, 0, 0, 0, 11, 129, 2, 2, 2, 2, 2, + 2, 2, 130, 23, 22, 20, 48, 131, 132, 133, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 52, 30, 2, 2, 2, 2, 2, 2, 2, 2, 10, 22, 59, + 98, 76, 134, 135, 136, 0, 0, 0, 0, 2, 137, 2, 2, 2, 2, 138, + 0, 30, 2, 42, 5, 0, 79, 15, 2, 139, 20, 53, 127, 139, 2, 2, + 140, 10, 9, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 141, 21, + 25, 0, 0, 142, 143, 0, 0, 0, 0, 2, 65, 45, 23, 80, 47, 144, + 0, 81, 81, 81, 81, 81, 81, 81, 81, 0, 0, 0, 0, 0, 0, 0, + 6, 119, 119, 119, 119, 120, 0, 0, 0, 2, 2, 2, 2, 2, 9, 2, + 2, 2, 9, 2, 30, 2, 2, 2, 2, 2, 30, 2, 2, 2, 30, 9, + 0, 127, 20, 27, 31, 0, 0, 145, 146, 2, 2, 30, 2, 30, 2, 2, + 2, 2, 2, 2, 0, 14, 37, 0, 147, 2, 2, 13, 37, 0, 30, 2, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 2, 2, + 9, 2, 2, 11, 41, 0, 0, 0, 0, 2, 2, 2, 0, 27, 22, 22, + 30, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 27, 38, + 0, 2, 2, 2, 115, 115, 115, 115, 115, 148, 2, 9, 0, 0, 0, 0, + 0, 2, 14, 14, 0, 0, 0, 0, 0, 9, 2, 2, 9, 2, 2, 2, + 2, 30, 2, 9, 0, 30, 2, 0, 0, 149, 150, 151, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 22, 22, 20, 20, 20, 22, 22, 133, 0, 0, 0, + 0, 0, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 2, 2, 2, 2, + 2, 53, 52, 53, 0, 0, 0, 0, 153, 11, 74, 2, 2, 2, 2, 2, + 2, 18, 19, 21, 16, 24, 37, 0, 0, 0, 31, 0, 0, 0, 0, 0, + 0, 11, 49, 2, 2, 2, 2, 2, 2, 2, 2, 2, 127, 20, 22, 154, + 22, 21, 155, 156, 2, 2, 2, 2, 2, 0, 0, 65, 157, 0, 0, 0, + 0, 2, 13, 0, 0, 0, 0, 0, 0, 2, 65, 25, 20, 20, 20, 22, + 22, 107, 158, 0, 0, 56, 159, 31, 160, 30, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 23, 19, 22, 22, 161, 44, 0, 0, 0, + 49, 127, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 9, 2, 2, + 30, 2, 2, 2, 2, 2, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, + 10, 18, 19, 21, 22, 162, 31, 0, 0, 11, 11, 30, 2, 2, 2, 9, + 30, 9, 2, 30, 2, 2, 58, 17, 23, 16, 23, 47, 32, 33, 32, 34, + 0, 0, 0, 0, 35, 0, 0, 0, 2, 2, 23, 0, 11, 11, 11, 46, + 0, 11, 11, 46, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 30, 0, + 9, 2, 2, 2, 30, 45, 59, 20, 20, 31, 33, 32, 32, 25, 163, 29, + 164, 165, 37, 0, 0, 0, 0, 0, 0, 12, 26, 0, 0, 0, 0, 0, + 0, 2, 2, 65, 25, 20, 20, 20, 22, 23, 125, 15, 17, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 0, 0, 166, 167, 0, 0, 0, 0, 0, 0, + 0, 18, 19, 20, 20, 66, 98, 25, 160, 11, 168, 9, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 65, 25, 20, 20, 0, 48, 48, 11, + 169, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 20, + 0, 23, 19, 20, 20, 21, 16, 82, 169, 38, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 10, 170, 25, 20, 22, 22, 168, 9, 0, 0, + 0, 2, 2, 2, 2, 2, 9, 43, 135, 23, 22, 20, 76, 21, 22, 0, + 0, 2, 2, 2, 9, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 18, + 19, 20, 21, 22, 104, 169, 37, 0, 0, 2, 2, 2, 9, 30, 0, 2, + 2, 2, 2, 30, 9, 2, 2, 2, 2, 23, 23, 18, 32, 33, 12, 171, + 165, 172, 173, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 2, 2, + 2, 65, 25, 20, 20, 0, 22, 23, 29, 107, 0, 33, 0, 0, 0, 0, + 0, 52, 20, 22, 22, 22, 139, 2, 2, 2, 174, 140, 11, 15, 175, 61, + 176, 0, 0, 1, 147, 0, 0, 0, 0, 52, 20, 22, 16, 19, 20, 2, + 2, 2, 2, 158, 158, 158, 177, 177, 177, 177, 177, 177, 15, 178, 0, 30, + 0, 16, 20, 16, 16, 0, 0, 0, 0, 22, 20, 20, 31, 22, 22, 11, 169, 0, 61, 61, 61, 61, 61, 61, 61, 66, 21, 82, 46, 0, 0, 0, 0, 2, 2, 2, 9, 2, 30, 2, 2, 52, 22, 22, 31, 0, 38, 22, - 27, 11, 159, 180, 181, 0, 0, 0, 0, 2, 2, 2, 30, 9, 2, 2, + 27, 11, 159, 179, 180, 0, 0, 0, 0, 2, 2, 2, 30, 9, 2, 2, 2, 2, 2, 2, 2, 2, 23, 23, 47, 22, 35, 82, 68, 0, 0, 0, - 0, 2, 182, 66, 47, 0, 0, 0, 0, 11, 183, 2, 2, 2, 2, 2, + 0, 2, 181, 66, 47, 0, 0, 0, 0, 11, 182, 2, 2, 2, 2, 2, 2, 2, 2, 23, 22, 20, 31, 0, 48, 16, 143, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 156, 0, 0, 184, 184, 184, 184, 184, 184, 184, - 184, 185, 185, 185, 186, 187, 185, 184, 184, 188, 184, 184, 189, 190, 190, 190, - 190, 190, 190, 190, 0, 0, 0, 0, 0, 184, 184, 184, 184, 184, 191, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, 22, 22, 192, 193, - 194, 11, 11, 11, 46, 0, 0, 0, 0, 29, 74, 2, 2, 2, 2, 2, + 0, 2, 2, 2, 2, 2, 156, 0, 0, 183, 183, 183, 183, 183, 183, 183, + 183, 184, 184, 184, 185, 186, 184, 183, 183, 187, 183, 183, 188, 189, 189, 189, + 189, 189, 189, 189, 0, 0, 0, 0, 0, 183, 183, 183, 183, 183, 190, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, 22, 22, 191, 192, + 193, 11, 11, 11, 46, 0, 0, 0, 0, 29, 74, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 65, 47, 0, 2, 2, 2, 2, 2, 9, 0, - 58, 195, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 0, 0, 0, 40, 116, 26, 0, 0, 0, 0, 0, + 58, 194, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 0, 0, 0, 40, 115, 26, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 30, 2, 2, 2, 2, 2, 0, 58, 37, 0, 6, 120, 120, 120, 121, 0, + 30, 2, 2, 2, 2, 2, 0, 58, 37, 0, 6, 119, 119, 119, 120, 0, 0, 11, 11, 11, 49, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 46, 2, 2, 2, 2, 2, 2, 11, 11, 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, @@ -300,23 +301,21 @@ hb_use_u8[3345] = VMBlw,VMPst, IS, VAbv, MPst, MPre, MBlw, MBlw, B, MBlw, MBlw, VPst,VMPst,VMPst, B, MBlw, VPst, VPre, VAbv, VAbv,VMPst,VMPst,VMBlw, B,VMPst, VBlw, VPst, CGJ, CGJ, VPst,VMAbv,VMAbv, FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS,FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB, - CMAbv,CMAbv, B, GB, B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, - VPre, B, MPre, MBlw, SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, - VPst, H, B, O,SMAbv,SMAbv,SMAbv, VPst, IS, RK, RK, VBlw, FAbv,VMPre,VMPre,FMAbv, - CMBlw,VMBlw,VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ, O,FMPst, O, SB, - SE, O, H, MPst, VPst, H,VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B, - CMAbv, VAbv, MBlw, MPst, MBlw, H, O, VBlw, MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, - FPst, VBlw, B, B, VPre, O,VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, + CMAbv,CMAbv, B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, VPre, B, + MPre, MBlw, SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, H, + B, O,SMAbv,SMAbv,SMAbv, VPst, IS, RK, RK, VBlw, FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw, + VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ, O,FMPst, O, SB, SE, O, + H, MPst, VPst, H,VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B,CMAbv, VAbv, + MBlw, MPst, MBlw, H, O, VBlw, MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, FPst, VBlw, + B, VBlw,VMAbv, B, VPre, O,VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, IS,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, B, N, N, O, HN, VPre, VBlw, VAbv, IS,CMAbv, O, VPst, B, R, R,CMBlw, VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,VMPst, O,VMAbv,CMBlw, IS, R,FMAbv, B, CS, CS, H,CMBlw,VMPst, H,VMPst, VAbv,VMAbv, - VPst, MPst, R, MPst,CMBlw, B,FMBlw, VBlw,VMAbv, CS, SUB, SUB, GB, FBlw, FBlw,CMAbv, - IS, VBlw, IS, R, MBlw, GB, VAbv, R,VMPst, G, G, J, J, J, SB, SE, - J, HR, G, G, HM, HM, HM, G, O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O, - VBlw, + VPst, MPst, R, MPst,CMBlw, B,FMBlw, CS, SUB, SUB, GB, FBlw, FBlw,CMAbv, IS, VBlw, + IS, R, MBlw, GB, VAbv, R,VMPst, G, G, J, J, J, SB, SE, J, HR, + G, G, HM, HM, HM, G, O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O, VBlw, }; -static const uint16_t -hb_use_u16[856] = +static const uint16_t hb_use_u16[864]= { 0, 0, 1, 2, 0, 3, 0, 3, 0, 0, 4, 5, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, @@ -330,66 +329,65 @@ hb_use_u16[856] = 73, 74, 75, 76, 77, 0, 0, 0, 10, 10, 78, 79, 80, 81, 82, 83, 84, 85, 0, 0, 0, 0, 0, 0, 10, 86, 10, 87, 10, 88, 89, 90, 10, 10, 10, 91, 92, 93, 2, 0, 94, 0, 10, 10, 10, 10, 10, 95, - 96, 10, 97, 0, 0, 0, 0, 0, 98, 99,100,101, 31, 10,102,103, - 10, 10,104, 10,105,106, 0, 0, 10,107, 10, 10, 10,108,109,110, - 2, 2, 0, 0, 0, 0, 0, 0,111, 10, 10,112,113, 2,114,115, - 116, 10,117, 10, 10, 10,118,119, 10, 10,120,121,122, 0, 0, 0, - 0, 0, 0, 0, 0,123,124,125, 0, 0, 0, 0, 0, 0, 0,126, - 127,128,129, 0, 0, 0,130,131,132, 0, 0, 0, 0, 0, 0,133, - 0, 0, 0, 0,134, 0, 0, 0, 0, 0, 0, 0, 0, 0,135, 0, - 0, 0, 0, 10, 10, 10,136,137, 0, 0,138, 0, 0, 0, 0, 0, - 139, 10,140, 0, 10, 10, 10,141,142, 10, 10,143,144, 2,145,146, - 10, 10,147, 10,148,149, 0, 0,150, 10, 10,151,152, 2,153, 99, - 10, 10,154,155,156, 2, 10,157, 10, 10, 10,158,159, 0,160,161, - 0, 0, 0, 0, 10, 10,162, 2,163, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,164, 0, 0, 0, 0, 0, 0, 0,165, - 0, 0, 0, 0, 0, 0, 0,166,166,167, 34,168, 0, 0, 0, 0, - 169,170, 10,171, 95, 0, 0, 0, 0, 0, 0, 0, 70, 10,172, 0, - 10,173,174, 0, 0, 0, 0, 0, 10, 10,175, 2, 9, 10,176, 10, - 177, 0, 0, 0, 0, 0, 0, 0, 10, 10,178,173, 0, 0, 0, 0, - 0, 0, 0, 10,179,180, 0, 10,181, 0, 0,182,183, 0, 0, 0, - 184, 10, 10,185,186,187,188,189,190, 10, 10,191,192, 0, 0, 0, - 193, 10,194,195,196, 10, 10,197,190, 10, 10,198,199,106,200,103, - 10, 34,201,202,203, 0, 0, 0,204,205, 95, 10, 10,206,207, 2, - 208, 21, 22,209,210,211,212,213,214, 10, 10,215,216,217,218, 0, - 10, 10, 10,219,220,221,222, 0,200, 10, 10,223,224, 2, 0, 0, - 10, 10,225,226,227,228, 0, 0, 10, 10, 10,229,230, 2, 0, 0, - 10, 10,231,232, 2, 10,141, 0, 10,233,234,104,235, 0, 0, 0, - 10, 10,236,237, 0, 0, 0, 0,238,239, 10,240,241, 2, 0, 0, - 0, 0,242, 10, 10,243,244, 0,245, 10, 10,246,247,248, 10, 10, - 249,250, 0, 0, 0, 0, 0, 0, 22, 10,225,251, 8, 10, 71, 19, - 10,252, 74,253, 0, 0, 0, 0,254, 10, 10,255,256, 2,257, 10, - 258,259, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,260, - 261, 49, 10,262,263,264, 0, 0,265,265,265,265,265,265,265,265, - 265,265,265,266,267,268,265,265,265,265,265,265,265,265,265,269, - 10,270,271, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, - 10, 10, 10,272, 0, 0, 0, 0, 0, 0, 0, 0,273, 10,274, 2, - 10, 10, 10, 10,275,276,277,277,278,279, 0, 0, 0, 0,280, 0, - 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,177, 0,281, - 10, 10, 10, 10, 10, 10,106, 71, 95,282, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,283, 10, 10, 71,284,285, 0, 0, 0, - 0, 10,286, 0, 10, 10,287, 2, 0, 0, 0, 0, 0, 10,288, 2, - 0, 0, 0, 0, 0, 10,289,106, 10, 10, 10, 10,290, 2, 0, 0, - 130,130,130,130,130,130,130,130,163,163,163,163,163,163,163,163, - 163,163,163,163,163,163,163,130, + 96, 10, 97, 0, 0, 0, 0, 0, 10, 98, 99,100, 31, 10,101,102, + 10, 10,103, 10,104,105, 0, 0, 10,106, 10, 10, 10,107,108,109, + 2, 2, 0, 0, 0, 0, 0, 0,110, 10, 10,111,112, 2,113,114, + 115, 10,116, 10, 10, 10,117,118, 10, 10,119,120,121, 0, 0, 0, + 0, 0, 0, 0, 0,122,123,124, 0, 0, 0, 0, 0, 0, 0,125, + 126,127,128, 0, 0, 0,129,130,131, 0, 0, 0, 0, 0, 0,132, + 0, 0, 0, 0,133, 0, 0, 0, 0, 0, 0, 0, 0, 0,134, 0, + 0, 0, 0, 10, 10, 10,135,136, 0, 0,137, 0, 0, 0, 0, 0, + 138, 10,139, 0, 10, 10, 10,140,141, 10, 10,142,143, 2,144,145, + 10, 10,146, 10,147,148, 0, 0,149, 10, 10,150,151, 2,152, 98, + 10, 10,153,154,155, 2, 10,156, 10, 10, 10,157,158, 0,159,160, + 0, 0, 0, 0, 10, 10,161, 2,162, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,163, 0, 0, 0, 0, 0, 0, 0,164, + 0, 0, 0, 0, 0, 0, 0,165,165,166, 34,167, 0, 0, 0, 0, + 168,169, 10,170, 95, 0, 0, 0, 0, 0, 0, 0, 70, 10,171, 0, + 10,172,173, 0, 0, 0, 0, 0, 10, 10,174, 2, 9, 10,175, 10, + 176, 0, 0, 0, 0, 0, 0, 0, 10, 10,177,172, 0, 0, 0, 0, + 0, 0, 0, 10,178,179, 0, 10,180, 0, 0,181,182, 0, 0, 0, + 183, 10, 10,184,185,186,187,188,189, 10, 10,190,191, 0, 0, 0, + 192, 10,193,194,195, 10, 10,196,189, 10, 10,197,198,105,199,102, + 10, 34,200,201,202, 0, 0, 0,203,204, 95, 10, 10,205,206, 2, + 207, 21, 22,208,209,210,211,212,213, 10, 10,214,215,216,217, 0, + 10, 10, 10,218,219,220,221, 0,199, 10, 10,222,223, 2, 0, 0, + 10, 10,224,225,226,227, 0, 0, 10, 10, 10,228,229, 2, 0, 0, + 10, 10,230,231, 2, 10,140, 0, 10,232,233,103,234, 0, 0, 0, + 10, 10,235,236, 0, 0, 0, 0,237,238, 10,239,240, 2, 0, 0, + 0, 0,241, 10, 10,242,243, 0,244, 10, 10,245,246,247, 10, 10, + 248,249, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,250, 0, + 22, 10,224,251, 8, 10, 71, 19, 10,252, 74,253, 0, 0, 0, 0, + 254, 10, 10,255,256, 2,257, 10,258,259, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 10,260,261, 49, 10,262,263,264, 0, 0, + 265,265,265,265,265,265,265,265,265,265,265,266,267,268,265,265, + 265,265,265,265,265,265,265,269, 10,270,271, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 0, 0, 0, 10, 10, 10,272, 0, 0, 0, 0, + 0, 0, 0, 0,273, 10,274, 2, 10, 10, 10, 10,275,276,277,277, + 278,279, 0, 0, 0, 0,280, 0, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10,176, 0,281, 10, 10, 10, 10, 10, 10,105, 71, + 95,282, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,283, + 10, 10, 71,284,285, 0, 0, 0, 0, 10,286, 0, 10, 10,287, 2, + 0, 0, 0, 0, 0, 10,288, 2, 0, 0, 0, 0, 0, 10,289,105, + 10, 10, 10, 10,290, 2, 0, 0,129,129,129,129,129,129,129,129, + 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,129, }; -static inline unsigned -hb_use_b4 (const uint8_t* a, unsigned i) +static inline uint8_t hb_use_b4 (const uint8_t* a, unsigned i) { - return (a[i>>1]>>((i&1u)<<2))&15u; + return (a[i>>1]>>((i&1)<<2))&15; } -static inline uint_fast8_t -hb_use_get_category (unsigned u) +static inline uint8_t hb_use_get_category (unsigned u) { - return u<921600u?hb_use_u8[2953+(((hb_use_u8[625+(((hb_use_u16[((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>3>>5))<<5)+((u>>1>>3>>3)&31u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O; + return u<921600 ? hb_use_u8[2953u+((hb_use_u8[625u+((hb_use_u16[((hb_use_u8[113u+((hb_use_b4(hb_use_u8,((((((((u)>>1))>>3))>>3))>>5)))<<5)+((((((((u)>>1))>>3))>>3))&31)])<<3)+((((((u)>>1))>>3))&7)])<<3)+((((u)>>1))&7)])<<1)+((u)&1)] : O; } #else -static const uint8_t -hb_use_u8[3657] = +#include + +static const uint8_t hb_use_u8[3663]= { 16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 57, 58, 59, 195, 211, 62, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, @@ -405,16 +403,16 @@ hb_use_u8[3657] = 1, 1, 1, 1, 1, 1, 1, 1, 1, 25, 26, 27, 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 29, 30, 1, 1, 1, 1, 1, 31, 1, 1, 1, 1, 32, 33, 1, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 1, 48, 49, 50, - 51, 52, 52, 52, 52, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 54, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 55, 1, 1, 1, 1, 1, 1, 1, 1, 56, 57, 1, 58, 1, - 59, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 60, 61, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 1, - 1, 1, 63, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 64, 65, 1, 66, 67, 1, 1, 1, 68, 1, 1, 1, 1, 1, - 1, 69, 70, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 0, 1, 2, 2, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 53, 53, 53, 54, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 55, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 56, 1, 1, 1, 1, 1, 1, 1, 1, 57, 58, 1, 59, 1, + 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 61, 62, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 63, 1, 1, + 1, 1, 64, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 65, 66, 1, 67, 68, 1, 1, 1, 69, 1, 1, 1, 1, 1, + 1, 70, 71, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 0, 1, 2, 2, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 0, 0, 9, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, @@ -438,120 +436,120 @@ hb_use_u8[3657] = 0, 0, 0, 0, 0, 56, 179, 180, 0, 56, 181, 182, 0, 56, 183, 184, 185, 186, 187, 188, 0, 0, 0, 0, 0, 56, 189, 0, 0, 0, 0, 0, 0, 190, 191, 192, 0, 0, 193, 194, 195, 196, 197, 198, 56, 199, 0, 0, - 0, 200, 201, 202, 203, 204, 205, 0, 0, 206, 207, 208, 209, 210, 67, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 211, 212, 213, 214, 0, 0, 0, 0, - 0, 215, 215, 215, 215, 215, 215, 215, 215, 215, 216, 217, 215, 215, 215, 215, - 215, 215, 215, 215, 215, 215, 215, 215, 218, 219, 220, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 67, 0, 56, 221, 0, 0, 0, 0, 0, - 0, 0, 0, 222, 223, 0, 0, 0, 0, 56, 56, 224, 225, 226, 0, 0, - 227, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 228, - 229, 56, 56, 56, 230, 231, 0, 0, 0, 0, 0, 0, 232, 0, 0, 0, - 0, 56, 233, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 235, 56, - 236, 0, 0, 0, 0, 0, 0, 101, 237, 0, 0, 0, 0, 0, 0, 101, - 238, 56, 56, 239, 0, 0, 0, 0, 0, 240, 240, 240, 240, 240, 240, 240, - 240, 241, 241, 241, 241, 241, 241, 241, 242, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 0, 0, 3, 4, 0, 5, 0, 0, 0, 0, 0, 6, - 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 10, 11, 11, 11, 11, 0, 0, 0, 9, 12, - 0, 2, 2, 2, 2, 13, 14, 0, 0, 11, 15, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 16, 17, 18, 19, 20, 21, 22, 16, 23, 24, - 25, 12, 26, 27, 20, 2, 2, 2, 2, 2, 20, 0, 2, 2, 2, 2, - 2, 0, 2, 2, 2, 2, 2, 2, 2, 28, 29, 30, 2, 2, 2, 9, - 30, 9, 30, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 9, 2, 2, - 2, 9, 9, 0, 2, 2, 0, 17, 18, 19, 20, 31, 32, 33, 32, 34, - 0, 0, 0, 0, 35, 0, 0, 2, 30, 2, 0, 0, 0, 0, 0, 9, - 36, 12, 15, 30, 2, 2, 9, 0, 30, 9, 2, 30, 9, 2, 0, 37, - 18, 19, 31, 0, 27, 38, 27, 39, 0, 40, 0, 0, 0, 30, 2, 9, - 9, 0, 0, 0, 2, 2, 2, 2, 2, 41, 42, 43, 0, 0, 0, 0, - 0, 12, 15, 30, 2, 2, 2, 2, 30, 2, 30, 2, 2, 2, 2, 2, - 2, 9, 2, 30, 2, 2, 0, 17, 18, 19, 20, 21, 27, 22, 35, 24, - 0, 0, 0, 0, 0, 30, 41, 41, 44, 12, 29, 30, 2, 2, 2, 9, - 30, 9, 2, 30, 2, 2, 0, 17, 45, 0, 0, 27, 22, 0, 0, 2, - 30, 30, 0, 0, 0, 0, 0, 0, 0, 0, 46, 30, 2, 2, 9, 0, - 2, 9, 2, 2, 0, 30, 9, 9, 2, 0, 30, 9, 0, 2, 9, 0, - 2, 2, 2, 2, 2, 2, 0, 0, 23, 16, 47, 0, 48, 33, 48, 34, - 0, 0, 0, 0, 35, 0, 0, 0, 0, 15, 29, 49, 2, 2, 2, 9, - 2, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 17, - 22, 16, 23, 47, 22, 38, 22, 39, 0, 0, 0, 27, 31, 2, 9, 0, - 0, 10, 29, 30, 2, 2, 2, 9, 2, 2, 2, 30, 2, 2, 0, 17, - 45, 0, 0, 35, 47, 0, 0, 0, 9, 50, 51, 0, 0, 0, 0, 0, - 0, 11, 29, 2, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 52, 53, - 23, 19, 20, 31, 48, 33, 48, 34, 54, 0, 0, 0, 35, 0, 0, 0, - 30, 12, 29, 30, 2, 2, 2, 2, 2, 2, 2, 2, 9, 0, 2, 2, - 2, 2, 30, 2, 2, 2, 2, 30, 0, 2, 2, 2, 9, 0, 55, 0, - 35, 23, 22, 31, 31, 18, 48, 48, 25, 0, 23, 0, 0, 0, 0, 0, - 0, 2, 0, 2, 9, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, - 0, 2, 2, 56, 56, 57, 0, 0, 18, 2, 2, 2, 2, 30, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 9, 0, 58, 21, 59, 22, 22, 20, 20, - 46, 21, 11, 31, 11, 2, 2, 60, 61, 61, 61, 61, 61, 62, 61, 61, - 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 63, - 0, 0, 0, 0, 64, 0, 0, 0, 0, 2, 2, 2, 2, 2, 65, 45, - 59, 66, 22, 22, 67, 68, 69, 70, 71, 2, 2, 2, 2, 2, 1, 0, - 5, 2, 2, 2, 23, 20, 2, 2, 72, 71, 73, 74, 65, 73, 29, 29, - 2, 52, 22, 53, 2, 2, 2, 2, 2, 2, 75, 76, 77, 29, 29, 78, - 79, 2, 2, 2, 2, 2, 29, 45, 0, 2, 59, 80, 0, 0, 0, 0, - 30, 2, 59, 47, 0, 0, 0, 0, 0, 2, 59, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 9, 2, 9, 59, 0, 0, 0, 0, 0, - 0, 2, 2, 81, 45, 22, 59, 20, 48, 48, 48, 48, 15, 82, 83, 84, - 85, 86, 87, 0, 0, 0, 0, 88, 0, 9, 0, 0, 30, 0, 89, 81, - 90, 2, 2, 2, 2, 9, 0, 0, 0, 42, 42, 91, 92, 2, 2, 2, - 2, 2, 2, 2, 2, 13, 9, 0, 0, 93, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 9, 22, 80, 45, 22, 94, 61, 0, - 0, 95, 96, 95, 95, 97, 98, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 200, 0, 0, 0, 0, 201, 202, 203, 204, 205, 206, 0, + 0, 207, 208, 209, 210, 211, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 212, 213, 214, 215, 0, 0, 0, 0, 0, 216, 216, 216, 216, 216, 216, 216, + 216, 216, 217, 218, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, + 219, 220, 221, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, + 0, 56, 222, 0, 0, 0, 0, 0, 0, 0, 0, 223, 224, 0, 0, 0, + 0, 56, 56, 225, 226, 227, 0, 0, 228, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 229, 230, 56, 56, 56, 231, 232, 0, 0, + 0, 0, 0, 0, 233, 0, 0, 0, 0, 56, 234, 235, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 101, 236, 56, 237, 0, 0, 0, 0, 0, 0, 101, + 238, 0, 0, 0, 0, 0, 0, 101, 239, 56, 56, 240, 0, 0, 0, 0, + 0, 241, 241, 241, 241, 241, 241, 241, 241, 242, 242, 242, 242, 242, 242, 242, + 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, + 0, 5, 0, 0, 0, 0, 0, 6, 0, 0, 7, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10, 11, + 11, 11, 11, 0, 0, 0, 9, 12, 0, 2, 2, 2, 2, 13, 14, 0, + 0, 11, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 16, 17, + 18, 19, 20, 21, 22, 16, 23, 24, 25, 12, 26, 27, 20, 2, 2, 2, + 2, 2, 20, 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, + 2, 28, 29, 30, 2, 2, 2, 9, 30, 9, 30, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 9, 2, 2, 2, 9, 9, 0, 2, 2, 0, 17, + 18, 19, 20, 31, 32, 33, 32, 34, 0, 0, 0, 0, 35, 0, 0, 2, + 30, 2, 0, 0, 0, 0, 0, 9, 36, 12, 15, 30, 2, 2, 9, 0, + 30, 9, 2, 30, 9, 2, 0, 37, 18, 19, 31, 0, 27, 38, 27, 39, + 0, 40, 0, 0, 0, 30, 2, 9, 9, 0, 0, 0, 2, 2, 2, 2, + 2, 41, 42, 43, 0, 0, 0, 0, 0, 12, 15, 30, 2, 2, 2, 2, + 30, 2, 30, 2, 2, 2, 2, 2, 2, 9, 2, 30, 2, 2, 0, 17, + 18, 19, 20, 21, 27, 22, 35, 24, 0, 0, 0, 0, 0, 30, 41, 41, + 44, 12, 29, 30, 2, 2, 2, 9, 30, 9, 2, 30, 2, 2, 0, 17, + 45, 0, 0, 27, 22, 0, 0, 2, 30, 30, 0, 0, 0, 0, 0, 0, + 0, 0, 46, 30, 2, 2, 9, 0, 2, 9, 2, 2, 0, 30, 9, 9, + 2, 0, 30, 9, 0, 2, 9, 0, 2, 2, 2, 2, 2, 2, 0, 0, + 23, 16, 47, 0, 48, 33, 48, 34, 0, 0, 0, 0, 35, 0, 0, 0, + 0, 15, 29, 49, 2, 2, 2, 9, 2, 9, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 17, 22, 16, 23, 47, 22, 38, 22, 39, + 0, 0, 0, 27, 31, 2, 9, 0, 0, 10, 29, 30, 2, 2, 2, 9, + 2, 2, 2, 30, 2, 2, 0, 17, 45, 0, 0, 35, 47, 0, 0, 0, + 9, 50, 51, 0, 0, 0, 0, 0, 0, 11, 29, 2, 2, 2, 2, 9, + 2, 2, 2, 2, 2, 2, 52, 53, 23, 19, 20, 31, 48, 33, 48, 34, + 54, 0, 0, 0, 35, 0, 0, 0, 30, 12, 29, 30, 2, 2, 2, 2, + 2, 2, 2, 2, 9, 0, 2, 2, 2, 2, 30, 2, 2, 2, 2, 30, + 0, 2, 2, 2, 9, 0, 55, 0, 35, 23, 22, 31, 31, 18, 48, 48, + 25, 0, 23, 0, 0, 0, 0, 0, 0, 2, 0, 2, 9, 0, 0, 0, + 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 2, 56, 56, 57, 0, 0, + 18, 2, 2, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, 2, 2, 9, + 0, 58, 21, 59, 22, 22, 20, 20, 46, 21, 11, 31, 11, 2, 2, 60, + 61, 61, 61, 61, 61, 62, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 63, 0, 0, 0, 0, 64, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 65, 45, 59, 66, 22, 22, 67, 68, 69, 70, + 71, 2, 2, 2, 2, 2, 1, 0, 5, 2, 2, 2, 23, 20, 2, 2, + 72, 71, 73, 74, 65, 73, 29, 29, 2, 52, 22, 53, 2, 2, 2, 2, + 2, 2, 75, 76, 77, 29, 29, 78, 79, 2, 2, 2, 2, 2, 29, 45, + 0, 2, 59, 80, 0, 0, 0, 0, 30, 2, 59, 47, 0, 0, 0, 0, + 0, 2, 59, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 9, + 2, 9, 59, 0, 0, 0, 0, 0, 0, 2, 2, 81, 45, 22, 59, 20, + 48, 48, 48, 48, 15, 82, 83, 84, 85, 86, 87, 0, 0, 0, 0, 88, + 0, 9, 0, 0, 30, 0, 89, 81, 90, 2, 2, 2, 2, 9, 0, 0, + 0, 42, 42, 91, 92, 2, 2, 2, 2, 2, 2, 2, 2, 13, 9, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 9, 22, 80, 45, 22, 93, 61, 0, + 0, 94, 95, 94, 94, 96, 97, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 9, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 29, 0, 0, 0, 2, 2, 2, 2, 2, 9, 0, - 0, 2, 2, 2, 52, 99, 45, 0, 0, 2, 2, 100, 101, 102, 103, 61, - 63, 104, 16, 45, 22, 59, 21, 80, 48, 48, 76, 11, 11, 11, 105, 46, - 40, 11, 106, 74, 2, 2, 2, 2, 2, 2, 2, 107, 22, 20, 20, 22, - 48, 48, 22, 108, 2, 2, 2, 9, 0, 0, 0, 0, 0, 0, 109, 110, - 110, 110, 110, 0, 0, 0, 0, 0, 0, 106, 74, 2, 2, 2, 2, 2, - 2, 60, 61, 59, 25, 22, 111, 61, 2, 2, 2, 2, 107, 22, 23, 45, - 45, 102, 112, 0, 0, 0, 0, 0, 0, 2, 2, 61, 18, 48, 23, 113, - 102, 102, 102, 114, 115, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 30, - 2, 11, 46, 116, 116, 116, 11, 116, 116, 15, 116, 116, 116, 26, 0, 40, - 0, 0, 0, 117, 51, 11, 5, 0, 0, 0, 0, 0, 0, 0, 118, 0, - 0, 0, 0, 0, 0, 0, 6, 119, 120, 42, 42, 5, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 120, 120, 121, 120, 120, 120, 120, 120, 120, 120, - 120, 0, 0, 122, 0, 0, 0, 0, 0, 0, 7, 122, 0, 0, 0, 0, + 0, 2, 2, 2, 52, 98, 45, 0, 0, 2, 2, 99, 100, 101, 102, 61, + 63, 103, 16, 45, 22, 59, 21, 80, 48, 48, 76, 11, 11, 11, 104, 46, + 40, 11, 105, 74, 2, 2, 2, 2, 2, 2, 2, 106, 22, 20, 20, 22, + 48, 48, 22, 107, 2, 2, 2, 9, 0, 0, 0, 0, 0, 0, 108, 109, + 109, 109, 109, 0, 0, 0, 0, 0, 0, 105, 74, 2, 2, 2, 2, 2, + 2, 60, 61, 59, 25, 22, 110, 61, 2, 2, 2, 2, 106, 22, 23, 45, + 45, 101, 111, 0, 0, 0, 0, 0, 0, 2, 2, 61, 18, 48, 23, 112, + 101, 101, 101, 113, 114, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 30, + 2, 11, 46, 115, 115, 115, 11, 115, 115, 15, 115, 115, 115, 26, 0, 40, + 0, 0, 0, 116, 51, 11, 5, 0, 0, 0, 0, 0, 0, 0, 117, 0, + 0, 0, 0, 0, 0, 0, 6, 118, 119, 42, 42, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 119, 120, 119, 119, 119, 119, 119, 119, 119, + 119, 0, 0, 121, 0, 0, 0, 0, 0, 0, 7, 121, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, - 0, 0, 0, 0, 123, 123, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, - 30, 0, 0, 0, 0, 0, 0, 0, 124, 0, 123, 123, 0, 0, 0, 0, - 0, 2, 53, 2, 108, 2, 10, 2, 2, 2, 65, 19, 16, 0, 0, 31, + 0, 0, 0, 0, 122, 122, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, + 30, 0, 0, 0, 0, 0, 0, 0, 123, 0, 122, 122, 0, 0, 0, 0, + 0, 2, 53, 2, 107, 2, 10, 2, 2, 2, 65, 19, 16, 0, 0, 31, 0, 2, 2, 0, 0, 0, 0, 0, 0, 29, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 125, 23, 23, 23, 23, 23, 23, 23, 126, 0, 0, 0, 0, + 2, 2, 2, 124, 23, 23, 23, 23, 23, 23, 23, 125, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 0, 0, 0, 0, 0, - 52, 2, 2, 2, 22, 22, 127, 116, 0, 2, 2, 2, 128, 20, 59, 20, - 113, 102, 129, 0, 0, 0, 0, 0, 0, 11, 130, 2, 2, 2, 2, 2, - 2, 2, 131, 23, 22, 20, 48, 132, 133, 134, 0, 0, 0, 0, 0, 0, + 52, 2, 2, 2, 22, 22, 126, 115, 0, 2, 2, 2, 127, 20, 59, 20, + 112, 101, 128, 0, 0, 0, 0, 0, 0, 11, 129, 2, 2, 2, 2, 2, + 2, 2, 130, 23, 22, 20, 48, 131, 132, 133, 0, 0, 0, 0, 0, 0, 0, 2, 2, 52, 30, 2, 2, 2, 2, 2, 2, 2, 2, 10, 22, 59, - 99, 76, 135, 136, 137, 0, 0, 0, 0, 2, 138, 2, 2, 2, 2, 139, - 0, 30, 2, 42, 5, 0, 79, 15, 2, 53, 22, 140, 52, 53, 2, 2, - 105, 10, 9, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 141, 21, + 98, 76, 134, 135, 136, 0, 0, 0, 0, 2, 137, 2, 2, 2, 2, 138, + 0, 30, 2, 42, 5, 0, 79, 15, 2, 139, 20, 53, 127, 139, 2, 2, + 140, 10, 9, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 141, 21, 25, 0, 0, 142, 143, 0, 0, 0, 0, 2, 65, 45, 23, 80, 47, 144, 0, 81, 81, 81, 81, 81, 81, 81, 81, 0, 0, 0, 0, 0, 0, 0, - 6, 120, 120, 120, 120, 121, 0, 0, 0, 2, 2, 2, 2, 2, 9, 2, + 6, 119, 119, 119, 119, 120, 0, 0, 0, 2, 2, 2, 2, 2, 9, 2, 2, 2, 9, 2, 30, 2, 2, 2, 2, 2, 30, 2, 2, 2, 30, 9, - 0, 128, 20, 27, 31, 0, 0, 145, 146, 2, 2, 30, 2, 30, 2, 2, + 0, 127, 20, 27, 31, 0, 0, 145, 146, 2, 2, 30, 2, 30, 2, 2, 2, 2, 2, 2, 0, 14, 37, 0, 147, 2, 2, 13, 37, 0, 30, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 2, 2, 9, 2, 2, 11, 41, 0, 0, 0, 0, 2, 2, 2, 0, 27, 22, 22, 30, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 27, 38, - 0, 2, 2, 2, 116, 116, 116, 116, 116, 148, 2, 9, 0, 0, 0, 0, + 0, 2, 2, 2, 115, 115, 115, 115, 115, 148, 2, 9, 0, 0, 0, 0, 0, 2, 14, 14, 0, 0, 0, 0, 0, 9, 2, 2, 9, 2, 2, 2, 2, 30, 2, 9, 0, 30, 2, 0, 0, 149, 150, 151, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 22, 22, 20, 20, 20, 22, 22, 134, 0, 0, 0, + 2, 2, 2, 2, 2, 22, 22, 20, 20, 20, 22, 22, 133, 0, 0, 0, 0, 0, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 2, 2, 2, 2, 2, 53, 52, 53, 0, 0, 0, 0, 153, 11, 74, 2, 2, 2, 2, 2, 2, 18, 19, 21, 16, 24, 37, 0, 0, 0, 31, 0, 0, 0, 0, 0, - 0, 11, 49, 2, 2, 2, 2, 2, 2, 2, 2, 2, 128, 20, 22, 154, + 0, 11, 49, 2, 2, 2, 2, 2, 2, 2, 2, 2, 127, 20, 22, 154, 22, 21, 155, 156, 2, 2, 2, 2, 2, 0, 0, 65, 157, 0, 0, 0, 0, 2, 13, 0, 0, 0, 0, 0, 0, 2, 65, 25, 20, 20, 20, 22, - 22, 108, 158, 0, 0, 56, 159, 31, 160, 30, 2, 2, 2, 2, 2, 2, + 22, 107, 158, 0, 0, 56, 159, 31, 160, 30, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 23, 19, 22, 22, 161, 44, 0, 0, 0, - 49, 128, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 9, 2, 2, + 49, 127, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 9, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, 10, 18, 19, 21, 22, 162, 31, 0, 0, 11, 11, 30, 2, 2, 2, 9, 30, 9, 2, 30, 2, 2, 58, 17, 23, 16, 23, 47, 32, 33, 32, 34, @@ -559,70 +557,69 @@ hb_use_u8[3657] = 0, 11, 11, 46, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 30, 0, 9, 2, 2, 2, 30, 45, 59, 20, 20, 31, 33, 32, 32, 25, 163, 29, 164, 165, 37, 0, 0, 0, 0, 0, 0, 12, 26, 0, 0, 0, 0, 0, - 0, 2, 2, 65, 25, 20, 20, 20, 22, 23, 126, 15, 17, 0, 0, 0, + 0, 2, 2, 65, 25, 20, 20, 20, 22, 23, 125, 15, 17, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 166, 167, 0, 0, 0, 0, 0, 0, - 0, 18, 19, 20, 20, 66, 99, 25, 160, 11, 168, 9, 0, 0, 0, 0, + 0, 18, 19, 20, 20, 66, 98, 25, 160, 11, 168, 9, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 65, 25, 20, 20, 0, 48, 48, 11, 169, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 20, 0, 23, 19, 20, 20, 21, 16, 82, 169, 38, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 10, 170, 25, 20, 22, 22, 168, 9, 0, 0, - 0, 2, 2, 2, 2, 2, 9, 43, 136, 23, 22, 20, 76, 21, 22, 0, + 0, 2, 2, 2, 2, 2, 9, 43, 135, 23, 22, 20, 76, 21, 22, 0, 0, 2, 2, 2, 9, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 18, - 19, 20, 21, 22, 105, 169, 37, 0, 0, 2, 2, 2, 9, 30, 0, 2, + 19, 20, 21, 22, 104, 169, 37, 0, 0, 2, 2, 2, 9, 30, 0, 2, 2, 2, 2, 30, 9, 2, 2, 2, 2, 23, 23, 18, 32, 33, 12, 171, 165, 172, 173, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 2, 2, - 2, 65, 25, 20, 20, 0, 22, 23, 29, 108, 0, 33, 0, 0, 0, 0, - 0, 52, 20, 22, 22, 22, 140, 2, 2, 2, 174, 175, 11, 15, 176, 61, - 177, 0, 0, 1, 147, 0, 0, 0, 0, 52, 20, 22, 16, 19, 20, 2, - 2, 2, 2, 158, 158, 158, 178, 178, 178, 178, 178, 178, 15, 179, 0, 30, - 0, 22, 20, 20, 31, 22, 22, 11, 169, 0, 61, 61, 61, 61, 61, 61, - 61, 66, 21, 82, 46, 0, 0, 0, 0, 2, 2, 2, 9, 2, 30, 2, - 2, 52, 22, 22, 31, 0, 38, 22, 27, 11, 159, 180, 181, 0, 0, 0, - 0, 2, 2, 2, 30, 9, 2, 2, 2, 2, 2, 2, 2, 2, 23, 23, - 47, 22, 35, 82, 68, 0, 0, 0, 0, 2, 182, 66, 47, 0, 0, 0, - 0, 11, 183, 2, 2, 2, 2, 2, 2, 2, 2, 23, 22, 20, 31, 0, - 48, 16, 143, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 156, 0, - 0, 184, 184, 184, 184, 184, 184, 184, 184, 185, 185, 185, 186, 187, 185, 184, - 184, 188, 184, 184, 189, 190, 190, 190, 190, 190, 190, 190, 0, 0, 0, 0, - 0, 184, 184, 184, 184, 184, 191, 0, 0, 2, 2, 2, 2, 2, 2, 2, - 22, 22, 22, 22, 22, 22, 192, 193, 194, 11, 11, 11, 46, 0, 0, 0, - 0, 29, 74, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 65, 47, - 0, 2, 2, 2, 2, 2, 9, 0, 58, 195, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, - 40, 116, 26, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 30, 2, 2, 2, 2, 2, 0, 58, - 37, 0, 6, 120, 120, 120, 121, 0, 0, 11, 11, 11, 49, 2, 2, 2, - 0, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, - 46, 2, 2, 2, 2, 2, 2, 11, 11, 2, 2, 2, 2, 2, 2, 22, - 22, 2, 2, 2, 2, 2, 2, 2, 20, 2, 2, 44, 44, 44, 92, 0, - 0, O, O, O, GB, B, B, O, SB, O, SE, GB, O, O, WJ,FMPst, - FMPst, O, CGJ, B, O, B,VMAbv,VMAbv,VMAbv, O,VMAbv, B,CMBlw,CMBlw,CMBlw,VMAbv, - VMPst, VAbv, VPst,CMBlw, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, - VPst, VPst, H, VPre, VPst,VMBlw, O, O, VAbv, GB,VMAbv,VMPst,VMPst, O, B, VBlw, - O, O, VPre, VPre, O, VPre, H, O, VPst,FMAbv, O,CMBlw, O, VAbv, O, VAbv, - H, O,VMBlw,VMAbv,CMAbv, GB, GB, O, MBlw,CMAbv,CMAbv, VPst, VAbv,VMAbv, O, VPst, - O, VPre, VPre,VMAbv, B, O, CS, CS,VMPst, B, VAbv, VAbv, B, R, O, HVM, - O, O,FMBlw, O,CMAbv, O,CMBlw, VAbv, VBlw, B, SUB, SUB, SUB, O, SUB, SUB, - O,FMBlw, O, B, VPst, VBlw, VPre,VMAbv,VMBlw,VMPst, IS, VAbv, MPst, MPre, MBlw, MBlw, - B, MBlw, MBlw, VPst,VMPst,VMPst, B, MBlw, VPst, VPre, VAbv, VAbv,VMPst,VMPst,VMBlw, B, - VMPst, VBlw, VPst, CGJ, CGJ, VPst,VMAbv,VMAbv,FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS, - FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB,CMAbv,CMAbv, B, GB, B, VAbv, SUB, FPst, - FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, VPre, B, MPre, MBlw, SUB, FAbv, FAbv, MAbv, - SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, H, B, O,SMAbv,SMAbv,SMAbv, VPst, - IS, RK, RK, VBlw, FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw,VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, - CGJ, WJ, WJ, WJ, O,FMPst, O, SB, SE, O, H, MPst, VPst, H,VMAbv, VAbv, - VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B,CMAbv, VAbv, MBlw, MPst, MBlw, H, O, VBlw, - MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, FPst, VBlw, B, B, VPre, O,VMPst, IS, - O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, IS,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, - B, N, N, O, HN, VPre, VBlw, VAbv, IS,CMAbv, O, VPst, B, R, R,CMBlw, - VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,VMPst, O,VMAbv,CMBlw, IS, R,FMAbv, B, CS, - CS, H,CMBlw,VMPst, H,VMPst, VAbv,VMAbv, VPst, MPst, R, MPst,CMBlw, B,FMBlw, VBlw, - VMAbv, CS, SUB, SUB, GB, FBlw, FBlw,CMAbv, IS, VBlw, IS, R, MBlw, GB, VAbv, R, - VMPst, G, G, J, J, J, SB, SE, J, HR, G, G, HM, HM, HM, G, - O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O, VBlw, + 2, 65, 25, 20, 20, 0, 22, 23, 29, 107, 0, 33, 0, 0, 0, 0, + 0, 52, 20, 22, 22, 22, 139, 2, 2, 2, 174, 140, 11, 15, 175, 61, + 176, 0, 0, 1, 147, 0, 0, 0, 0, 52, 20, 22, 16, 19, 20, 2, + 2, 2, 2, 158, 158, 158, 177, 177, 177, 177, 177, 177, 15, 178, 0, 30, + 0, 16, 20, 16, 16, 0, 0, 0, 0, 22, 20, 20, 31, 22, 22, 11, + 169, 0, 61, 61, 61, 61, 61, 61, 61, 66, 21, 82, 46, 0, 0, 0, + 0, 2, 2, 2, 9, 2, 30, 2, 2, 52, 22, 22, 31, 0, 38, 22, + 27, 11, 159, 179, 180, 0, 0, 0, 0, 2, 2, 2, 30, 9, 2, 2, + 2, 2, 2, 2, 2, 2, 23, 23, 47, 22, 35, 82, 68, 0, 0, 0, + 0, 2, 181, 66, 47, 0, 0, 0, 0, 11, 182, 2, 2, 2, 2, 2, + 2, 2, 2, 23, 22, 20, 31, 0, 48, 16, 143, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 156, 0, 0, 183, 183, 183, 183, 183, 183, 183, + 183, 184, 184, 184, 185, 186, 184, 183, 183, 187, 183, 183, 188, 189, 189, 189, + 189, 189, 189, 189, 0, 0, 0, 0, 0, 183, 183, 183, 183, 183, 190, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, 22, 22, 191, 192, + 193, 11, 11, 11, 46, 0, 0, 0, 0, 29, 74, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 65, 47, 0, 2, 2, 2, 2, 2, 9, 0, + 58, 194, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 0, 0, 0, 40, 115, 26, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 30, 2, 2, 2, 2, 2, 0, 58, 37, 0, 6, 119, 119, 119, 120, 0, + 0, 11, 11, 11, 49, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 46, 2, 2, 2, 2, 2, 2, 11, + 11, 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, + 20, 2, 2, 44, 44, 44, 92, 0, 0, O, O, O, GB, B, B, O, + SB, O, SE, GB, O, O, WJ,FMPst,FMPst, O, CGJ, B, O, B,VMAbv,VMAbv, + VMAbv, O,VMAbv, B,CMBlw,CMBlw,CMBlw,VMAbv,VMPst, VAbv, VPst,CMBlw, B, VPst, VPre, VPst, + VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, VPst, VPst, H, VPre, VPst,VMBlw, O, O, + VAbv, GB,VMAbv,VMPst,VMPst, O, B, VBlw, O, O, VPre, VPre, O, VPre, H, O, + VPst,FMAbv, O,CMBlw, O, VAbv, O, VAbv, H, O,VMBlw,VMAbv,CMAbv, GB, GB, O, + MBlw,CMAbv,CMAbv, VPst, VAbv,VMAbv, O, VPst, O, VPre, VPre,VMAbv, B, O, CS, CS, + VMPst, B, VAbv, VAbv, B, R, O, HVM, O, O,FMBlw, O,CMAbv, O,CMBlw, VAbv, + VBlw, B, SUB, SUB, SUB, O, SUB, SUB, O,FMBlw, O, B, VPst, VBlw, VPre,VMAbv, + VMBlw,VMPst, IS, VAbv, MPst, MPre, MBlw, MBlw, B, MBlw, MBlw, VPst,VMPst,VMPst, B, MBlw, + VPst, VPre, VAbv, VAbv,VMPst,VMPst,VMBlw, B,VMPst, VBlw, VPst, CGJ, CGJ, VPst,VMAbv,VMAbv, + FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS,FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB, + CMAbv,CMAbv, B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, VPre, B, + MPre, MBlw, SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, H, + B, O,SMAbv,SMAbv,SMAbv, VPst, IS, RK, RK, VBlw, FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw, + VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ, O,FMPst, O, SB, SE, O, + H, MPst, VPst, H,VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B,CMAbv, VAbv, + MBlw, MPst, MBlw, H, O, VBlw, MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, FPst, VBlw, + B, VBlw,VMAbv, B, VPre, O,VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, + IS,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, B, N, N, O, HN, VPre, VBlw, VAbv, + IS,CMAbv, O, VPst, B, R, R,CMBlw, VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,VMPst, + O,VMAbv,CMBlw, IS, R,FMAbv, B, CS, CS, H,CMBlw,VMPst, H,VMPst, VAbv,VMAbv, + VPst, MPst, R, MPst,CMBlw, B,FMBlw, CS, SUB, SUB, GB, FBlw, FBlw,CMAbv, IS, VBlw, + IS, R, MBlw, GB, VAbv, R,VMPst, G, G, J, J, J, SB, SE, J, HR, + G, G, HM, HM, HM, G, O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O, VBlw, }; -static const uint16_t -hb_use_u16[486] = +static const uint16_t hb_use_u16[488]= { 0, 0, 1, 2, 0, 3, 4, 5, 0, 6, 7, 0, 8, 0, 9, 10, 11, 12, 10, 13, 14, 10, 10, 15, 16, 17, 18, 19, 20, 21, 22, 23, @@ -633,43 +630,43 @@ hb_use_u16[486] = 31, 66, 67, 68, 10, 69, 70, 10, 71, 72, 73, 74, 75, 76, 77, 0, 10, 10, 78, 79, 80, 81, 82, 83, 84, 85, 10, 86, 10, 87, 10, 88, 89, 90, 10, 91, 92, 93, 2, 0, 94, 0, 10, 95, 96, 10, 97, 0, - 98, 99,100,101, 31, 10,102,103,104, 10,105,106, 10,107, 10,108, - 109,110, 2, 2,111, 10, 10,112,113, 2,114,115,116, 10,117, 10, - 118,119,120,121,122, 0, 0,123,124,125, 0,126,127,128,129, 0, - 130,131,132, 0, 0,133,134, 0,135, 0, 0, 10,136,137,138, 0, - 139, 10,140, 0, 10,141,142, 10, 10,143,144, 2,145,146,147, 10, - 148,149,150, 10, 10,151,152, 2,153, 99,154,155,156, 2, 10,157, - 10,158,159, 0,160,161,162, 2,163, 0, 0,164, 0,165, 0,166, - 166,167, 34,168,169,170, 10,171, 95, 0,172, 0, 10,173,174, 0, - 175, 2,176, 10,177, 0,178,173,179,180,181, 0, 0,182,183, 0, - 184, 10, 10,185,186,187,188,189,190, 10, 10,191,192, 0,193, 10, - 194,195,196, 10, 10,197, 10,198,199,106,200,103, 10, 34,201,202, - 203, 0,204,205, 95, 10, 10,206,207, 2,208, 21, 22,209,210,211, - 212,213,214, 10, 10,215,216,217,218, 0, 10,219,220,221,222, 0, - 200, 10, 10,223,224, 2,225,226,227,228, 10,229,230, 2,231,232, - 2, 10,141, 0, 10,233,234,104,235, 0,236,237,238,239, 10,240, - 241, 2,242, 10, 10,243,244, 0,245, 10, 10,246,247,248,249,250, - 22, 10,225,251, 8, 10, 71, 19, 10,252, 74,253,254, 10, 10,255, - 256, 2,257, 10,258,259, 10,260,261, 49, 10,262,263,264,265,265, - 265,266,267,268,265,269, 10,270,271, 2, 10,272,273, 10,274, 2, - 275,276,277,277,278,279,280, 0, 10,177, 0,281,106, 71, 95,282, - 0,283, 71,284,285, 0,286, 0,287, 2,288, 2,289,106,290, 2, - 130,130,163,163,163,130, + 10, 98, 99,100, 31, 10,101,102,103, 10,104,105, 10,106, 10,107, + 108,109, 2, 2,110, 10, 10,111,112, 2,113,114,115, 10,116, 10, + 117,118,119,120,121, 0, 0,122,123,124, 0,125,126,127,128, 0, + 129,130,131, 0, 0,132,133, 0,134, 0, 0, 10,135,136,137, 0, + 138, 10,139, 0, 10,140,141, 10, 10,142,143, 2,144,145,146, 10, + 147,148,149, 10, 10,150,151, 2,152, 98,153,154,155, 2, 10,156, + 10,157,158, 0,159,160,161, 2,162, 0, 0,163, 0,164, 0,165, + 165,166, 34,167,168,169, 10,170, 95, 0,171, 0, 10,172,173, 0, + 174, 2,175, 10,176, 0,177,172,178,179,180, 0, 0,181,182, 0, + 183, 10, 10,184,185,186,187,188,189, 10, 10,190,191, 0,192, 10, + 193,194,195, 10, 10,196, 10,197,198,105,199,102, 10, 34,200,201, + 202, 0,203,204, 95, 10, 10,205,206, 2,207, 21, 22,208,209,210, + 211,212,213, 10, 10,214,215,216,217, 0, 10,218,219,220,221, 0, + 199, 10, 10,222,223, 2,224,225,226,227, 10,228,229, 2,230,231, + 2, 10,140, 0, 10,232,233,103,234, 0,235,236,237,238, 10,239, + 240, 2,241, 10, 10,242,243, 0,244, 10, 10,245,246,247,248,249, + 250, 0, 22, 10,224,251, 8, 10, 71, 19, 10,252, 74,253,254, 10, + 10,255,256, 2,257, 10,258,259, 10,260,261, 49, 10,262,263,264, + 265,265,265,266,267,268,265,269, 10,270,271, 2, 10,272,273, 10, + 274, 2,275,276,277,277,278,279,280, 0, 10,176, 0,281,105, 71, + 95,282, 0,283, 71,284,285, 0,286, 0,287, 2,288, 2,289,105, + 290, 2,129,129,162,162,162,129, }; -static inline unsigned -hb_use_b4 (const uint8_t* a, unsigned i) +static inline uint8_t hb_use_b4 (const uint8_t* a, unsigned i) { - return (a[i>>1]>>((i&1u)<<2))&15u; + return (a[i>>1]>>((i&1)<<2))&15; } -static inline uint_fast8_t -hb_use_get_category (unsigned u) +static inline uint8_t hb_use_get_category (unsigned u) { - return u<921600u?hb_use_u8[3265+(((hb_use_u8[937+(((hb_use_u16[((hb_use_u8[369+(((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>1>>3>>4))<<4)+((u>>1>>3>>1>>3)&15u))])<<3)+((u>>1>>3>>1)&7u))])<<1)+((u>>1>>3)&1u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O; + return u<921600 ? hb_use_u8[3273u+((hb_use_u8[945u+((hb_use_u16[((hb_use_u8[369u+((hb_use_u8[113u+((hb_use_b4(hb_use_u8,((((((((((u)>>1))>>3))>>1))>>3))>>4)))<<4)+((((((((((u)>>1))>>3))>>1))>>3))&15)])<<3)+((((((((u)>>1))>>3))>>1))&7)])<<1)+((((((u)>>1))>>3))&1)])<<3)+((((u)>>1))&7)])<<1)+((u)&1)] : O; } + #endif + #undef B #undef CGJ #undef CS diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-vowel-constraints.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-vowel-constraints.cc index 4a112e1c289..bb586a68ce5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-vowel-constraints.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-vowel-constraints.cc @@ -10,8 +10,8 @@ * # Date: 2015-03-12, 21:17:00 GMT [AG] * # Date: 2019-11-08, 23:22:00 GMT [AG] * - * # Scripts-16.0.0.txt - * # Date: 2024-04-30, 21:48:40 GMT + * # Scripts-17.0.0.txt + * # Date: 2025-07-24, 13:28:55 GMT */ #include "hb.hh" diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper.hh index 48cd5296ffd..9993507e360 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper.hh @@ -396,6 +396,12 @@ hb_ot_shaper_categorize (hb_script_t script, case HB_SCRIPT_TODHRI: case HB_SCRIPT_TULU_TIGALARI: + /* Unicode-17.0 additions */ + case HB_SCRIPT_BERIA_ERFE: + case HB_SCRIPT_SIDETIC: + case HB_SCRIPT_TAI_YO: + case HB_SCRIPT_TOLONG_SIKI: + /* If the designer designed the font for the 'DFLT' script, * (or we ended up arbitrarily pick 'latn'), use the default shaper. * Otherwise, use the specific shaper. diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh index 10165f57b75..28bc23d8f01 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh @@ -352,7 +352,7 @@ struct AxisValue { float get_value (unsigned int axis_index) const { - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_value (); case 2: hb_barrier (); return u.format2.get_value (); @@ -364,7 +364,7 @@ struct AxisValue unsigned int get_axis_index () const { - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_axis_index (); case 2: hb_barrier (); return u.format2.get_axis_index (); @@ -376,7 +376,7 @@ struct AxisValue hb_ot_name_id_t get_value_name_id () const { - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_value_name_id (); case 2: hb_barrier (); return u.format2.get_value_name_id (); @@ -389,9 +389,9 @@ struct AxisValue 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) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward (ds)...)); case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward (ds)...)); @@ -403,7 +403,7 @@ struct AxisValue bool keep_axis_value (const hb_array_t axis_records, hb_hashmap_t *user_axes_location) const { - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return u.format1.keep_axis_value (axis_records, user_axes_location); case 2: hb_barrier (); return u.format2.keep_axis_value (axis_records, user_axes_location); @@ -420,7 +420,7 @@ struct AxisValue return_trace (false); hb_barrier (); - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); @@ -433,14 +433,14 @@ struct AxisValue protected: union { - HBUINT16 format; + struct { HBUINT16 v; } format; AxisValueFormat1 format1; AxisValueFormat2 format2; AxisValueFormat3 format3; AxisValueFormat4 format4; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; struct AxisValueOffsetArray: UnsizedArrayOf> diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh index 50ddf5f6967..4933e661371 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh @@ -6,8 +6,8 @@ * * on files with these headers: * - * - * File-Date: 2025-01-21 + * + * File-Date: 2025-08-25 */ #ifndef HB_OT_TAG_TABLE_HH @@ -704,7 +704,7 @@ static const LangTag ot_languages3[] = { /*{HB_TAG('g','u','z',' '), HB_TAG('G','U','Z',' ')},*/ /* Gusii */ {HB_TAG('g','w','i',' '), HB_TAG('A','T','H',' ')}, /* Gwichʼin -> Athapaskan */ {HB_TAG('g','y','n',' '), HB_TAG('C','P','P',' ')}, /* Guyanese Creole English -> Creoles */ - {HB_TAG('h','a','a',' '), HB_TAG('A','T','H',' ')}, /* Han -> Athapaskan */ + {HB_TAG('h','a','a',' '), HB_TAG('A','T','H',' ')}, /* Hän -> Athapaskan */ {HB_TAG('h','a','e',' '), HB_TAG('O','R','O',' ')}, /* Eastern Oromo -> Oromo */ {HB_TAG('h','a','i',' '), HB_TAG('H','A','I','0')}, /* Haida [macrolanguage] */ {HB_TAG('h','a','k',' '), HB_TAG('Z','H','S',' ')}, /* Hakka Chinese -> Chinese, Simplified */ @@ -921,7 +921,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('k','v','t',' '), HB_TAG('K','R','N',' ')}, /* Lahta Karen -> Karen */ {HB_TAG('k','v','u',' '), HB_TAG('K','R','N',' ')}, /* Yinbaw Karen -> Karen */ {HB_TAG('k','v','y',' '), HB_TAG('K','R','N',' ')}, /* Yintale Karen -> Karen */ -/*{HB_TAG('k','w','k',' '), HB_TAG('K','W','K',' ')},*/ /* Kwakiutl -> Kwakʼwala */ +/*{HB_TAG('k','w','k',' '), HB_TAG('K','W','K',' ')},*/ /* Kwakʼwala */ {HB_TAG('k','w','w',' '), HB_TAG('C','P','P',' ')}, /* Kwinti -> Creoles */ {HB_TAG('k','w','y',' '), HB_TAG('K','O','N','0')}, /* San Salvador Kongo -> Kongo */ {HB_TAG('k','x','c',' '), HB_TAG('K','M','S',' ')}, /* Konso -> Komso */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh index e26687c34ed..8a9307d6933 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh @@ -143,10 +143,13 @@ struct AxisValueMap struct SegmentMaps : Array16Of { - int map (int value, unsigned int from_offset = 0, unsigned int to_offset = 1) const + float map_float (float value, unsigned int from_offset = 0, unsigned int to_offset = 1) const { -#define fromCoord coords[from_offset].to_int () -#define toCoord coords[to_offset].to_int () +#define fromCoord coords[from_offset].to_float () +#define toCoord coords[to_offset].to_float () + + const auto *map = arrayZ; + /* The following special-cases are not part of OpenType, which requires * that at least -1, 0, and +1 must be mapped. But we include these as * part of a better error recovery scheme. */ @@ -155,47 +158,98 @@ struct SegmentMaps : Array16Of if (!len) return value; else /* len == 1*/ - return value - arrayZ[0].fromCoord + arrayZ[0].toCoord; + return value - map[0].fromCoord + map[0].toCoord; } - if (value <= arrayZ[0].fromCoord) - return value - arrayZ[0].fromCoord + arrayZ[0].toCoord; + // At least two mappings now. - unsigned int i; - unsigned int count = len - 1; - for (i = 1; i < count && value > arrayZ[i].fromCoord; i++) - ; + /* CoreText is wild... + * PingFangUI avar needs all this special-casing... + * So we implement an extended version of the spec here, + * which is more robust and more likely to be compatible with + * the wild. */ - if (value >= arrayZ[i].fromCoord) - return value - arrayZ[i].fromCoord + arrayZ[i].toCoord; + unsigned start = 0; + unsigned end = len; + if (map[start].fromCoord == -1 && map[start].toCoord == -1 && map[start+1].fromCoord == -1) + start++; + if (map[end-1].fromCoord == +1 && map[end-1].toCoord == +1 && map[end-2].fromCoord == +1) + end--; - if (unlikely (arrayZ[i-1].fromCoord == arrayZ[i].fromCoord)) - return arrayZ[i-1].toCoord; + /* Look for exact match first, and do lots of special-casing. */ + unsigned i; + for (i = start; i < end; i++) + if (value == map[i].fromCoord) + break; + if (i < end) + { + // There's at least one exact match. See if there are more. + unsigned j = i; + for (; j + 1 < end; j++) + if (value != map[j + 1].fromCoord) + break; + + // [i,j] inclusive are all exact matches: + + // If there's only one, return it. This is the only spec-compliant case. + if (i == j) + return map[i].toCoord; + // If there's exactly three, return the middle one. + if (i + 2 == j) + return map[i + 1].toCoord; + + // Ignore the middle ones. Return the one mapping closer to 0. + if (value < 0) return map[j].toCoord; + if (value > 0) return map[i].toCoord; + + // Mapping 0? CoreText seems confused. It seems to prefer 0 here... + // So we'll just return the smallest one. lol + return fabsf (map[i].toCoord) < fabsf (map[j].toCoord) ? map[i].toCoord : map[j].toCoord; + + // Mapping 0? Return one not mapping to 0. + if (map[i].toCoord == 0) + return map[j].toCoord; + else + return map[i].toCoord; + } + + /* There's at least two and we're not an exact match. Prepare to lerp. */ + + // Find the segment we're in. + for (i = start; i < end; i++) + if (value < map[i].fromCoord) + break; + + if (i == 0) + { + // Value before all segments; Shift. + return value - map[0].fromCoord + map[0].toCoord; + } + if (i == end) + { + // Value after all segments; Shift. + return value - map[end - 1].fromCoord + map[end - 1].toCoord; + } + + // Actually interpolate. + auto &before = map[i-1]; + auto &after = map[i]; + float denom = after.fromCoord - before.fromCoord; // Can't be zero by now. + return before.toCoord + ((after.toCoord - before.toCoord) * (value - before.fromCoord)) / denom; - int denom = arrayZ[i].fromCoord - arrayZ[i-1].fromCoord; - return roundf (arrayZ[i-1].toCoord + ((float) (arrayZ[i].toCoord - arrayZ[i-1].toCoord) * - (value - arrayZ[i-1].fromCoord)) / denom); #undef toCoord #undef fromCoord } - int unmap (int value) const { return map (value, 1, 0); } + float unmap_float (float value) const { return map_float (value, 1, 0); } + + // TODO Kill this. Triple unmap_axis_range (const Triple& axis_range) const { - F2DOT14 val, unmapped_val; - - val.set_float (axis_range.minimum); - unmapped_val.set_int (unmap (val.to_int ())); - float unmapped_min = unmapped_val.to_float (); - - val.set_float (axis_range.middle); - unmapped_val.set_int (unmap (val.to_int ())); - float unmapped_middle = unmapped_val.to_float (); - - val.set_float (axis_range.maximum); - unmapped_val.set_int (unmap (val.to_int ())); - float unmapped_max = unmapped_val.to_float (); + float unmapped_min = unmap_float (axis_range.minimum); + float unmapped_middle = unmap_float (axis_range.middle); + float unmapped_max = unmap_float (axis_range.maximum); return Triple{(double) unmapped_min, (double) unmapped_middle, (double) unmapped_max}; } @@ -203,6 +257,11 @@ struct SegmentMaps : Array16Of bool subset (hb_subset_context_t *c, hb_tag_t axis_tag) const { TRACE_SUBSET (this); + + /* This function cannot work on avar2 table (and currently doesn't). + * We should instead keep the design coords in the shape plan and use + * those. unmap_axis_range needs to be killed. */ + /* avar mapped normalized axis range*/ Triple *axis_range; if (!c->plan->axes_location.has (axis_tag, &axis_range)) @@ -304,14 +363,14 @@ struct avar return_trace (true); } - void map_coords (int *coords, unsigned int coords_length) const + void map_coords_16_16 (int *coords, unsigned int coords_length) const { unsigned int count = hb_min (coords_length, axisCount); const SegmentMaps *map = &firstAxisSegmentMaps; for (unsigned int i = 0; i < count; i++) { - coords[i] = map->map (coords[i]); + coords[i] = roundf (map->map_float (coords[i] / 65536.f) * 65536.f); map = &StructAfter (*map); } @@ -329,15 +388,20 @@ struct avar const auto &var_store = this+v2.varStore; auto *var_store_cache = var_store.create_cache (); + hb_vector_t coords_2_14; + coords_2_14.resize (coords_length); + for (unsigned i = 0; i < coords_length; i++) + coords_2_14[i] = roundf (coords[i] / 4.f); // 16.16 -> 2.14 + hb_vector_t out; out.alloc (coords_length); for (unsigned i = 0; i < coords_length; i++) { int v = coords[i]; uint32_t varidx = varidx_map.map (i); - float delta = var_store.get_delta (varidx, coords, coords_length, var_store_cache); - v += roundf (delta); - v = hb_clamp (v, -(1<<14), +(1<<14)); + float delta = var_store.get_delta (varidx, coords_2_14.arrayZ, coords_2_14.length, var_store_cache); + v += roundf (delta * 4); // 2.14 -> 16.16 + v = hb_clamp (v, -(1<<16), +(1<<16)); out.push (v); } for (unsigned i = 0; i < coords_length; i++) @@ -347,16 +411,54 @@ struct avar #endif } - void unmap_coords (int *coords, unsigned int coords_length) const + bool has_v2_data () const { return version.major > 1; } + + // axis normalization is done in 2.14 here + // TODO: deprecate this API once fonttools is updated to use 16.16 normalization + bool map_coords_2_14 (float *coords, unsigned int coords_length) const { + hb_vector_t coords_2_14; + if (!coords_2_14.resize (coords_length)) return false; unsigned int count = hb_min (coords_length, axisCount); const SegmentMaps *map = &firstAxisSegmentMaps; for (unsigned int i = 0; i < count; i++) { - coords[i] = map->unmap (coords[i]); + int v = roundf (map->map_float (coords[i]) * 16384.f); + coords_2_14[i] = v; + coords[i] = v / 16384.f; map = &StructAfter (*map); } + +#ifndef HB_NO_AVAR2 + if (version.major < 2) + return true; + hb_barrier (); + + for (; count < axisCount; count++) + map = &StructAfter (*map); + + const auto &v2 = * (const avarV2Tail *) map; + + const auto &varidx_map = this+v2.varIdxMap; + const auto &var_store = this+v2.varStore; + auto *var_store_cache = var_store.create_cache (); + + for (unsigned i = 0; i < coords_length; i++) + { + int v = coords_2_14[i]; + uint32_t varidx = varidx_map.map (i); + float delta = var_store.get_delta (varidx, coords_2_14.arrayZ, coords_2_14.length, var_store_cache); + v += roundf (delta); + v = hb_clamp (v, -(1<<16), +(1<<16)); + coords[i] = v / 16384.f; + } + + OT::ItemVariationStore::destroy_cache (var_store_cache); + return true; +#else + return version.major < 2; +#endif } bool subset (hb_subset_context_t *c) const diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-common.hh index 72deddef213..d0a4224c9b0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-common.hh @@ -27,24 +27,35 @@ #define HB_OT_VAR_COMMON_HH #include "hb-ot-layout-common.hh" +#include "hb-alloc-pool.hh" #include "hb-priority-queue.hh" #include "hb-subset-instancer-iup.hh" namespace OT { +using rebase_tent_result_scratch_t = hb_pair_t; /* https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuplevariationheader */ struct TupleVariationHeader { friend struct tuple_delta_t; - unsigned get_size (unsigned axis_count) const - { return min_size + get_all_tuples (axis_count).get_size (); } + unsigned get_size (unsigned axis_count_times_2) const + { + // This function is super hot in mega-var-fonts with hundreds of masters. + unsigned ti = tupleIndex; + if (unlikely ((ti & (TupleIndex::EmbeddedPeakTuple | TupleIndex::IntermediateRegion)))) + { + unsigned count = ((ti & TupleIndex::EmbeddedPeakTuple) != 0) + ((ti & TupleIndex::IntermediateRegion) != 0) * 2; + return min_size + count * axis_count_times_2; + } + return min_size; + } unsigned get_data_size () const { return varDataSize; } - const TupleVariationHeader &get_next (unsigned axis_count) const - { return StructAtOffset (this, get_size (axis_count)); } + const TupleVariationHeader &get_next (unsigned axis_count_times_2) const + { return StructAtOffset (this, get_size (axis_count_times_2)); } bool unpack_axis_tuples (unsigned axis_count, const hb_array_t shared_tuples, @@ -53,7 +64,7 @@ struct TupleVariationHeader { const F2DOT14 *peak_tuple = nullptr; if (has_peak ()) - peak_tuple = get_peak_tuple (axis_count).arrayZ; + peak_tuple = get_peak_tuple (axis_count); else { unsigned int index = get_index (); @@ -68,8 +79,8 @@ struct TupleVariationHeader if (has_interm) { - start_tuple = get_start_tuple (axis_count).arrayZ; - end_tuple = get_end_tuple (axis_count).arrayZ; + start_tuple = get_start_tuple (axis_count); + end_tuple = get_end_tuple (axis_count); } for (unsigned i = 0; i < axis_count; i++) @@ -98,88 +109,109 @@ struct TupleVariationHeader return true; } + HB_ALWAYS_INLINE double calculate_scalar (hb_array_t coords, unsigned int coord_count, const hb_array_t shared_tuples, - const hb_vector_t> *shared_tuple_active_idx = nullptr) const + hb_scalar_cache_t *shared_tuple_scalar_cache = nullptr) const { + unsigned tuple_index = tupleIndex; + const F2DOT14 *peak_tuple; - unsigned start_idx = 0; - unsigned end_idx = coord_count; - unsigned step = 1; + bool has_interm = tuple_index & TupleIndex::IntermediateRegion; // Inlined for performance - if (has_peak ()) - peak_tuple = get_peak_tuple (coord_count).arrayZ; + if (unlikely (tuple_index & TupleIndex::EmbeddedPeakTuple)) // Inlined for performance + { + peak_tuple = get_peak_tuple (coord_count); + shared_tuple_scalar_cache = nullptr; + } else { - unsigned int index = get_index (); + unsigned int index = tuple_index & TupleIndex::TupleIndexMask; // Inlined for performance + + float scalar; + if (shared_tuple_scalar_cache && + shared_tuple_scalar_cache->get (index, &scalar)) + { + if (has_interm && (scalar != 0 && scalar != 1.f)) + shared_tuple_scalar_cache = nullptr; + else + return (double) scalar; + } + if (unlikely ((index + 1) * coord_count > shared_tuples.length)) return 0.0; - peak_tuple = shared_tuples.sub_array (coord_count * index, coord_count).arrayZ; + peak_tuple = shared_tuples.arrayZ + (coord_count * index); - if (shared_tuple_active_idx) - { - if (unlikely (index >= shared_tuple_active_idx->length)) - return 0.0; - auto _ = (*shared_tuple_active_idx).arrayZ[index]; - if (_.second != -1) - { - start_idx = _.first; - end_idx = _.second + 1; - step = _.second - _.first; - } - else if (_.first != -1) - { - start_idx = _.first; - end_idx = start_idx + 1; - } - } } const F2DOT14 *start_tuple = nullptr; const F2DOT14 *end_tuple = nullptr; - bool has_interm = has_intermediate (); + if (has_interm) { - start_tuple = get_start_tuple (coord_count).arrayZ; - end_tuple = get_end_tuple (coord_count).arrayZ; + start_tuple = get_start_tuple (coord_count); + end_tuple = get_end_tuple (coord_count); } double scalar = 1.0; - for (unsigned int i = start_idx; i < end_idx; i += step) +#ifndef HB_OPTIMIZE_SIZE +#if HB_FAST_NUM_ACCESS + bool skip = coord_count >= 16; +#endif +#endif + for (unsigned int i = 0; i < coord_count; i++) { +#ifndef HB_OPTIMIZE_SIZE +#if HB_FAST_NUM_ACCESS + if (skip) + { + while (i + 4 <= coord_count && * (HBUINT64LE *) &peak_tuple[i] == 0) + i += 4; + while (i < coord_count && peak_tuple[i].to_int () == 0) + i += 1; + if (i >= coord_count) + break; + } +#endif +#endif + int peak = peak_tuple[i].to_int (); if (!peak) continue; int v = coords[i]; + if (!v) { scalar = 0.0; break; } if (v == peak) continue; if (has_interm) { + shared_tuple_scalar_cache = nullptr; int start = start_tuple[i].to_int (); int end = end_tuple[i].to_int (); if (unlikely (start > peak || peak > end || (start < 0 && end > 0 && peak))) continue; - if (v < start || v > end) return 0.0; + if (v < start || v > end) { scalar = 0.0; break; } if (v < peak) { if (peak != start) scalar *= (double) (v - start) / (peak - start); } else { if (peak != end) scalar *= (double) (end - v) / (end - peak); } } - else if (!v || v < hb_min (0, peak) || v > hb_max (0, peak)) return 0.0; + else if (v < hb_min (0, peak) || v > hb_max (0, peak)) { scalar = 0.0; break; } else scalar *= (double) v / peak; } + if (shared_tuple_scalar_cache) + shared_tuple_scalar_cache->set (get_index (), scalar); return scalar; } - bool has_peak () const { return tupleIndex & TuppleIndex::EmbeddedPeakTuple; } - bool has_intermediate () const { return tupleIndex & TuppleIndex::IntermediateRegion; } - bool has_private_points () const { return tupleIndex & TuppleIndex::PrivatePointNumbers; } - unsigned get_index () const { return tupleIndex & TuppleIndex::TupleIndexMask; } + bool has_peak () const { return tupleIndex & TupleIndex::EmbeddedPeakTuple; } + bool has_intermediate () const { return tupleIndex & TupleIndex::IntermediateRegion; } + bool has_private_points () const { return tupleIndex & TupleIndex::PrivatePointNumbers; } + unsigned get_index () const { return tupleIndex & TupleIndex::TupleIndexMask; } protected: - struct TuppleIndex : HBUINT16 + struct TupleIndex : HBUINT16 { enum Flags { EmbeddedPeakTuple = 0x8000u, @@ -188,22 +220,24 @@ struct TupleVariationHeader TupleIndexMask = 0x0FFFu }; - TuppleIndex& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } + TupleIndex& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } DEFINE_SIZE_STATIC (2); }; hb_array_t get_all_tuples (unsigned axis_count) const { return StructAfter> (tupleIndex).as_array ((has_peak () + has_intermediate () * 2) * axis_count); } - hb_array_t get_peak_tuple (unsigned axis_count) const - { return get_all_tuples (axis_count).sub_array (0, axis_count); } - hb_array_t get_start_tuple (unsigned axis_count) const - { return get_all_tuples (axis_count).sub_array (has_peak () * axis_count, axis_count); } - hb_array_t get_end_tuple (unsigned axis_count) const - { return get_all_tuples (axis_count).sub_array (has_peak () * axis_count + axis_count, axis_count); } + const F2DOT14* get_all_tuples_base (unsigned axis_count) const + { return StructAfter> (tupleIndex).arrayZ; } + const F2DOT14* get_peak_tuple (unsigned axis_count) const + { return get_all_tuples_base (axis_count); } + const F2DOT14* get_start_tuple (unsigned axis_count) const + { return get_all_tuples_base (axis_count) + has_peak () * axis_count; } + const F2DOT14* get_end_tuple (unsigned axis_count) const + { return get_all_tuples_base (axis_count) + has_peak () * axis_count + axis_count; } HBUINT16 varDataSize; /* The size in bytes of the serialized * data for this tuple variation table. */ - TuppleIndex tupleIndex; /* A packed field. The high 4 bits are flags (see below). + TupleIndex tupleIndex; /* A packed field. The high 4 bits are flags (see below). The low 12 bits are an index into a shared tuple records array. */ /* UnsizedArrayOf peakTuple - optional */ @@ -221,6 +255,21 @@ struct TupleVariationHeader DEFINE_SIZE_MIN (4); }; +struct optimize_scratch_t +{ + iup_scratch_t iup; + hb_vector_t opt_indices; + hb_vector_t rounded_x_deltas; + hb_vector_t rounded_y_deltas; + hb_vector_t opt_deltas_x; + hb_vector_t opt_deltas_y; + hb_vector_t opt_point_data; + hb_vector_t opt_deltas_data; + hb_vector_t point_data; + hb_vector_t deltas_data; + hb_vector_t rounded_deltas; +}; + struct tuple_delta_t { static constexpr bool realloc_move = true; // Watch out when adding new members! @@ -241,11 +290,12 @@ struct tuple_delta_t hb_vector_t compiled_tuple_header; hb_vector_t compiled_deltas; - /* compiled peak coords, empty for non-gvar tuples */ - hb_vector_t compiled_peak_coords; + hb_vector_t compiled_peak_coords; + hb_vector_t compiled_interm_coords; - tuple_delta_t () = default; + tuple_delta_t (hb_alloc_pool_t *pool = nullptr) {} tuple_delta_t (const tuple_delta_t& o) = default; + tuple_delta_t& operator = (const tuple_delta_t& o) = default; friend void swap (tuple_delta_t& a, tuple_delta_t& b) noexcept { @@ -267,6 +317,18 @@ struct tuple_delta_t return *this; } + void copy_from (const tuple_delta_t& o, hb_alloc_pool_t *pool = nullptr) + { + axis_tuples = o.axis_tuples; + indices.duplicate_vector_from_pool (pool, o.indices); + deltas_x.duplicate_vector_from_pool (pool, o.deltas_x); + deltas_y.duplicate_vector_from_pool (pool, o.deltas_y); + compiled_tuple_header.duplicate_vector_from_pool (pool, o.compiled_tuple_header); + compiled_deltas.duplicate_vector_from_pool (pool, o.compiled_deltas); + compiled_peak_coords.duplicate_vector_from_pool (pool, o.compiled_peak_coords); + compiled_interm_coords.duplicate_vector_from_pool (pool, o.compiled_interm_coords); + } + void remove_axis (hb_tag_t axis_tag) { axis_tuples.del (axis_tag); } @@ -321,31 +383,44 @@ struct tuple_delta_t return *this; } - hb_vector_t change_tuple_var_axis_limit (hb_tag_t axis_tag, Triple axis_limit, - TripleDistances axis_triple_distances) const + void change_tuple_var_axis_limit (hb_tag_t axis_tag, Triple axis_limit, + TripleDistances axis_triple_distances, + hb_vector_t& out, + rebase_tent_result_scratch_t &scratch, + hb_alloc_pool_t *pool = nullptr) { - hb_vector_t out; + // May move *this out. + + out.reset (); Triple *tent; if (!axis_tuples.has (axis_tag, &tent)) { - out.push (*this); - return out; + out.push (std::move (*this)); + return; } if ((tent->minimum < 0.0 && tent->maximum > 0.0) || !(tent->minimum <= tent->middle && tent->middle <= tent->maximum)) - return out; + return; if (tent->middle == 0.0) { - out.push (*this); - return out; + out.push (std::move (*this)); + return; } - rebase_tent_result_t solutions = rebase_tent (*tent, axis_limit, axis_triple_distances); - for (auto &t : solutions) + rebase_tent_result_t &solutions = scratch.first; + rebase_tent (*tent, axis_limit, axis_triple_distances, solutions, scratch.second); + for (unsigned i = 0; i < solutions.length; i++) { - tuple_delta_t new_var = *this; + auto &t = solutions.arrayZ[i]; + + tuple_delta_t new_var; + if (i < solutions.length - 1) + new_var.copy_from (*this, pool); + else + new_var = std::move (*this); + if (t.second == Triple ()) new_var.remove_axis (axis_tag); else @@ -354,38 +429,76 @@ struct tuple_delta_t new_var *= t.first; out.push (std::move (new_var)); } - - return out; } - bool compile_peak_coords (const hb_map_t& axes_index_map, - const hb_map_t& axes_old_index_tag_map) + bool compile_coords (const hb_map_t& axes_index_map, + const hb_map_t& axes_old_index_tag_map, + hb_alloc_pool_t *pool= nullptr) { - unsigned axis_count = axes_index_map.get_population (); - if (unlikely (!compiled_peak_coords.alloc (axis_count * F2DOT14::static_size))) + unsigned cur_axis_count = axes_index_map.get_population (); + if (pool) + { + if (unlikely (!compiled_peak_coords.allocate_from_pool (pool, cur_axis_count))) + return false; + } + else if (unlikely (!compiled_peak_coords.resize (cur_axis_count))) return false; + hb_array_t start_coords, end_coords; + unsigned orig_axis_count = axes_old_index_tag_map.get_population (); + unsigned j = 0; for (unsigned i = 0; i < orig_axis_count; i++) { if (!axes_index_map.has (i)) continue; hb_tag_t axis_tag = axes_old_index_tag_map.get (i); - Triple *coords; - F2DOT14 peak_coord; + Triple *coords = nullptr; if (axis_tuples.has (axis_tag, &coords)) - peak_coord.set_float (coords->middle); - else - peak_coord.set_int (0); + { + float min_val = coords->minimum; + float val = coords->middle; + float max_val = coords->maximum; - /* push F2DOT14 value into char vector */ - int16_t val = peak_coord.to_int (); - compiled_peak_coords.push (static_cast (val >> 8)); - compiled_peak_coords.push (static_cast (val & 0xFF)); + compiled_peak_coords.arrayZ[j].set_float (val); + + if (min_val != hb_min (val, 0.f) || max_val != hb_max (val, 0.f)) + { + if (!compiled_interm_coords) + { + if (pool) + { + if (unlikely (!compiled_interm_coords.allocate_from_pool (pool, 2 * cur_axis_count))) + return false; + } + else if (unlikely (!compiled_interm_coords.resize (2 * cur_axis_count))) + return false; + start_coords = compiled_interm_coords.as_array ().sub_array (0, cur_axis_count); + end_coords = compiled_interm_coords.as_array ().sub_array (cur_axis_count); + + for (unsigned k = 0; k < j; k++) + { + signed peak = compiled_peak_coords.arrayZ[k].to_int (); + if (!peak) continue; + start_coords.arrayZ[k].set_int (hb_min (peak, 0)); + end_coords.arrayZ[k].set_int (hb_max (peak, 0)); + } + } + + } + + if (compiled_interm_coords) + { + start_coords.arrayZ[j].set_float (min_val); + end_coords.arrayZ[j].set_float (max_val); + } + } + + j++; } - return !compiled_peak_coords.in_error (); + return !compiled_peak_coords.in_error () && !compiled_interm_coords.in_error (); } /* deltas should be compiled already before we compile tuple @@ -394,7 +507,8 @@ struct tuple_delta_t bool compile_tuple_var_header (const hb_map_t& axes_index_map, unsigned points_data_length, const hb_map_t& axes_old_index_tag_map, - const hb_hashmap_t*, unsigned>* shared_tuples_idx_map) + const hb_hashmap_t*, unsigned>* shared_tuples_idx_map, + hb_alloc_pool_t *pool = nullptr) { /* compiled_deltas could be empty after iup delta optimization, we can skip * compiling this tuple and return true */ @@ -403,7 +517,7 @@ struct tuple_delta_t unsigned cur_axis_count = axes_index_map.get_population (); /* allocate enough memory: 1 peak + 2 intermediate coords + fixed header size */ unsigned alloc_len = 3 * cur_axis_count * (F2DOT14::static_size) + 4; - if (unlikely (!compiled_tuple_header.resize (alloc_len))) return false; + if (unlikely (!compiled_tuple_header.allocate_from_pool (pool, alloc_len, false))) return false; unsigned flag = 0; /* skip the first 4 header bytes: variationDataSize+tupleIndex */ @@ -411,6 +525,9 @@ struct tuple_delta_t F2DOT14* end = reinterpret_cast (compiled_tuple_header.end ()); hb_array_t coords (p, end - p); + if (!shared_tuples_idx_map) + compile_coords (axes_index_map, axes_old_index_tag_map); // non-gvar tuples do not have compiled coords yet + /* encode peak coords */ unsigned peak_count = 0; unsigned *shared_tuple_idx; @@ -421,16 +538,16 @@ struct tuple_delta_t } else { - peak_count = encode_peak_coords(coords, flag, axes_index_map, axes_old_index_tag_map); + peak_count = encode_peak_coords(coords, flag); if (!peak_count) return false; } /* encode interim coords, it's optional so returned num could be 0 */ - unsigned interim_count = encode_interm_coords (coords.sub_array (peak_count), flag, axes_index_map, axes_old_index_tag_map); + unsigned interim_count = encode_interm_coords (coords.sub_array (peak_count), flag); /* pointdata length = 0 implies "use shared points" */ if (points_data_length) - flag |= TupleVariationHeader::TuppleIndex::PrivatePointNumbers; + flag |= TupleVariationHeader::TupleIndex::PrivatePointNumbers; unsigned serialized_data_size = points_data_length + compiled_deltas.length; TupleVariationHeader *o = reinterpret_cast (compiled_tuple_header.begin ()); @@ -438,105 +555,63 @@ struct tuple_delta_t o->tupleIndex = flag; unsigned total_header_len = 4 + (peak_count + interim_count) * (F2DOT14::static_size); - return compiled_tuple_header.resize (total_header_len); + compiled_tuple_header.shrink_back_to_pool (pool, total_header_len); + return true; } unsigned encode_peak_coords (hb_array_t peak_coords, - unsigned& flag, - const hb_map_t& axes_index_map, - const hb_map_t& axes_old_index_tag_map) const + unsigned& flag) const { - unsigned orig_axis_count = axes_old_index_tag_map.get_population (); - auto it = peak_coords.iter (); - unsigned count = 0; - for (unsigned i = 0; i < orig_axis_count; i++) - { - if (!axes_index_map.has (i)) /* axis pinned */ - continue; - hb_tag_t axis_tag = axes_old_index_tag_map.get (i); - Triple *coords; - if (!axis_tuples.has (axis_tag, &coords)) - (*it).set_int (0); - else - (*it).set_float (coords->middle); - it++; - count++; - } - flag |= TupleVariationHeader::TuppleIndex::EmbeddedPeakTuple; - return count; + hb_memcpy (&peak_coords[0], &compiled_peak_coords[0], compiled_peak_coords.length * sizeof (compiled_peak_coords[0])); + flag |= TupleVariationHeader::TupleIndex::EmbeddedPeakTuple; + return compiled_peak_coords.length; } /* if no need to encode intermediate coords, then just return p */ unsigned encode_interm_coords (hb_array_t coords, - unsigned& flag, - const hb_map_t& axes_index_map, - const hb_map_t& axes_old_index_tag_map) const + unsigned& flag) const { - unsigned orig_axis_count = axes_old_index_tag_map.get_population (); - unsigned cur_axis_count = axes_index_map.get_population (); - - auto start_coords_iter = coords.sub_array (0, cur_axis_count).iter (); - auto end_coords_iter = coords.sub_array (cur_axis_count).iter (); - bool encode_needed = false; - unsigned count = 0; - for (unsigned i = 0; i < orig_axis_count; i++) + if (compiled_interm_coords) { - if (!axes_index_map.has (i)) /* axis pinned */ - continue; - hb_tag_t axis_tag = axes_old_index_tag_map.get (i); - Triple *coords; - float min_val = 0.f, val = 0.f, max_val = 0.f; - if (axis_tuples.has (axis_tag, &coords)) - { - min_val = coords->minimum; - val = coords->middle; - max_val = coords->maximum; - } - - (*start_coords_iter).set_float (min_val); - (*end_coords_iter).set_float (max_val); - - start_coords_iter++; - end_coords_iter++; - count += 2; - if (min_val != hb_min (val, 0.f) || max_val != hb_max (val, 0.f)) - encode_needed = true; + hb_memcpy (&coords[0], &compiled_interm_coords[0], compiled_interm_coords.length * sizeof (compiled_interm_coords[0])); + flag |= TupleVariationHeader::TupleIndex::IntermediateRegion; } - - if (encode_needed) - { - flag |= TupleVariationHeader::TuppleIndex::IntermediateRegion; - return count; - } - return 0; + return compiled_interm_coords.length; } - bool compile_deltas () - { return compile_deltas (indices, deltas_x, deltas_y, compiled_deltas); } + bool compile_deltas (hb_vector_t &rounded_deltas_scratch, + hb_alloc_pool_t *pool = nullptr) + { return compile_deltas (indices, deltas_x, deltas_y, compiled_deltas, rounded_deltas_scratch, pool); } static bool compile_deltas (hb_array_t point_indices, hb_array_t x_deltas, hb_array_t y_deltas, - hb_vector_t &compiled_deltas /* OUT */) + hb_vector_t &compiled_deltas, /* OUT */ + hb_vector_t &rounded_deltas, /* scratch */ + hb_alloc_pool_t *pool = nullptr) { - hb_vector_t rounded_deltas; - if (unlikely (!rounded_deltas.alloc (point_indices.length))) + if (unlikely (!rounded_deltas.resize_dirty (point_indices.length))) return false; + unsigned j = 0; for (unsigned i = 0; i < point_indices.length; i++) { if (!point_indices[i]) continue; - int rounded_delta = (int) roundf (x_deltas.arrayZ[i]); - rounded_deltas.push (rounded_delta); + rounded_deltas.arrayZ[j++] = (int) roundf (x_deltas.arrayZ[i]); } + rounded_deltas.resize (j); if (!rounded_deltas) return true; - /* allocate enough memories 5 * num_deltas */ - unsigned alloc_len = 5 * rounded_deltas.length; + /* Allocate enough memory: this is the correct bound: + * Worst case scenario is that each delta has to be encoded in 4 bytes, and there + * are runs of 64 items each. Any delta encoded in less than 4 bytes (2, 1, or 0) + * is still smaller than the 4-byte encoding even with their control byte. + * The initial 2 is to handle length==0, for both x and y deltas. */ + unsigned alloc_len = 2 + 4 * rounded_deltas.length + (rounded_deltas.length + 63) / 64; if (y_deltas) alloc_len *= 2; - if (unlikely (!compiled_deltas.resize (alloc_len))) return false; + if (unlikely (!compiled_deltas.allocate_from_pool (pool, alloc_len, false))) return false; unsigned encoded_len = compile_deltas (compiled_deltas, rounded_deltas); @@ -557,28 +632,30 @@ struct tuple_delta_t if (j != rounded_deltas.length) return false; encoded_len += compile_deltas (compiled_deltas.as_array ().sub_array (encoded_len), rounded_deltas); } - return compiled_deltas.resize (encoded_len); + compiled_deltas.shrink_back_to_pool (pool, encoded_len); + return true; } static unsigned compile_deltas (hb_array_t encoded_bytes, hb_array_t deltas) { - return TupleValues::compile (deltas, encoded_bytes); + return TupleValues::compile_unsafe (deltas, encoded_bytes); } - bool calc_inferred_deltas (const contour_point_vector_t& orig_points) + bool calc_inferred_deltas (const contour_point_vector_t& orig_points, + hb_vector_t &scratch) { unsigned point_count = orig_points.length; if (point_count != indices.length) return false; unsigned ref_count = 0; - hb_vector_t end_points; + + hb_vector_t &end_points = scratch.reset (); for (unsigned i = 0; i < point_count; i++) { - if (indices.arrayZ[i]) - ref_count++; + ref_count += indices.arrayZ[i]; if (orig_points.arrayZ[i].is_end_point) end_points.push (i); } @@ -587,7 +664,7 @@ struct tuple_delta_t return true; if (unlikely (end_points.in_error ())) return false; - hb_set_t inferred_idxes; + hb_bit_set_t inferred_idxes; unsigned start_point = 0; for (unsigned end_point : end_points) { @@ -661,6 +738,7 @@ struct tuple_delta_t bool optimize (const contour_point_vector_t& contour_points, bool is_composite, + optimize_scratch_t &scratch, double tolerance = 0.5 + 1e-10) { unsigned count = contour_points.length; @@ -668,22 +746,21 @@ struct tuple_delta_t deltas_y.length != count) return false; - hb_vector_t opt_indices; - hb_vector_t rounded_x_deltas, rounded_y_deltas; + hb_vector_t &opt_indices = scratch.opt_indices.reset (); + hb_vector_t &rounded_x_deltas = scratch.rounded_x_deltas; + hb_vector_t &rounded_y_deltas = scratch.rounded_y_deltas; - if (unlikely (!rounded_x_deltas.alloc (count) || - !rounded_y_deltas.alloc (count))) + if (unlikely (!rounded_x_deltas.resize_dirty (count) || + !rounded_y_deltas.resize_dirty (count))) return false; for (unsigned i = 0; i < count; i++) { - int rounded_x_delta = (int) roundf (deltas_x.arrayZ[i]); - int rounded_y_delta = (int) roundf (deltas_y.arrayZ[i]); - rounded_x_deltas.push (rounded_x_delta); - rounded_y_deltas.push (rounded_y_delta); + rounded_x_deltas.arrayZ[i] = (int) roundf (deltas_x.arrayZ[i]); + rounded_y_deltas.arrayZ[i] = (int) roundf (deltas_y.arrayZ[i]); } - if (!iup_delta_optimize (contour_points, rounded_x_deltas, rounded_y_deltas, opt_indices, tolerance)) + if (!iup_delta_optimize (contour_points, rounded_x_deltas, rounded_y_deltas, opt_indices, scratch.iup, tolerance)) return false; unsigned ref_count = 0; @@ -692,7 +769,8 @@ struct tuple_delta_t if (ref_count == count) return true; - hb_vector_t opt_deltas_x, opt_deltas_y; + hb_vector_t &opt_deltas_x = scratch.opt_deltas_x.reset (); + hb_vector_t &opt_deltas_y = scratch.opt_deltas_y.reset (); bool is_comp_glyph_wo_deltas = (is_composite && ref_count == 0); if (is_comp_glyph_wo_deltas) { @@ -705,34 +783,31 @@ struct tuple_delta_t opt_indices.arrayZ[i] = false; } - hb_vector_t opt_point_data; + hb_vector_t &opt_point_data = scratch.opt_point_data.reset (); if (!compile_point_set (opt_indices, opt_point_data)) return false; - hb_vector_t opt_deltas_data; + hb_vector_t &opt_deltas_data = scratch.opt_deltas_data.reset (); if (!compile_deltas (opt_indices, is_comp_glyph_wo_deltas ? opt_deltas_x : deltas_x, is_comp_glyph_wo_deltas ? opt_deltas_y : deltas_y, - opt_deltas_data)) + opt_deltas_data, + scratch.rounded_deltas)) return false; - hb_vector_t point_data; + hb_vector_t &point_data = scratch.point_data.reset (); if (!compile_point_set (indices, point_data)) return false; - hb_vector_t deltas_data; - if (!compile_deltas (indices, deltas_x, deltas_y, deltas_data)) + hb_vector_t &deltas_data = scratch.deltas_data.reset (); + if (!compile_deltas (indices, deltas_x, deltas_y, deltas_data, scratch.rounded_deltas)) return false; if (opt_point_data.length + opt_deltas_data.length < point_data.length + deltas_data.length) { - indices.fini (); indices = std::move (opt_indices); if (is_comp_glyph_wo_deltas) { - deltas_x.fini (); deltas_x = std::move (opt_deltas_x); - - deltas_y.fini (); deltas_y = std::move (opt_deltas_y); } } @@ -757,7 +832,7 @@ struct tuple_delta_t /* allocate enough memories: 2 bytes for count + 3 bytes for each point */ unsigned num_bytes = 2 + 3 *num_points; - if (unlikely (!compiled_points.resize (num_bytes, false))) + if (unlikely (!compiled_points.resize_dirty (num_bytes))) return false; unsigned pos = 0; @@ -821,7 +896,7 @@ struct tuple_delta_t else compiled_points.arrayZ[header_pos] = (run_length - 1) | 0x80; } - return compiled_points.resize (pos, false); + return compiled_points.resize_dirty (pos); } static double infer_delta (double target_val, double prev_val, double next_val, double prev_delta, double next_delta) @@ -852,15 +927,15 @@ struct TupleVariationData return_trace (c->check_struct (this)); } - unsigned get_size (unsigned axis_count) const + unsigned get_size (unsigned axis_count_times_2) const { unsigned total_size = min_size; unsigned count = tupleVarCount.get_count (); const TupleVariationHeader *tuple_var_header = &(get_tuple_var_header()); for (unsigned i = 0; i < count; i++) { - total_size += tuple_var_header->get_size (axis_count) + tuple_var_header->get_data_size (); - tuple_var_header = &tuple_var_header->get_next (axis_count); + total_size += tuple_var_header->get_size (axis_count_times_2) + tuple_var_header->get_data_size (); + tuple_var_header = &tuple_var_header->get_next (axis_count_times_2); } return total_size; @@ -925,8 +1000,12 @@ struct TupleVariationData const hb_map_t *axes_old_index_tag_map, const hb_vector_t &shared_indices, const hb_array_t shared_tuples, - bool is_composite_glyph) + hb_alloc_pool_t *pool = nullptr, + bool is_composite_glyph = false) { + hb_vector_t private_indices; + hb_vector_t deltas_x; + hb_vector_t deltas_y; do { const HBUINT8 *p = iterator.get_serialized_data (); @@ -939,7 +1018,7 @@ struct TupleVariationData || axis_tuples.is_empty ()) return false; - hb_vector_t private_indices; + private_indices.reset (); bool has_private_points = iterator.current_tuple->has_private_points (); const HBUINT8 *end = p + length; if (has_private_points && @@ -950,27 +1029,24 @@ struct TupleVariationData bool apply_to_all = (indices.length == 0); unsigned num_deltas = apply_to_all ? point_count : indices.length; - hb_vector_t deltas_x; - - if (unlikely (!deltas_x.resize (num_deltas, false) || + if (unlikely (!deltas_x.resize_dirty (num_deltas) || !TupleVariationData::decompile_deltas (p, deltas_x, end))) return false; - hb_vector_t deltas_y; if (is_gvar) { - if (unlikely (!deltas_y.resize (num_deltas, false) || + if (unlikely (!deltas_y.resize_dirty (num_deltas) || !TupleVariationData::decompile_deltas (p, deltas_y, end))) return false; } tuple_delta_t var; var.axis_tuples = std::move (axis_tuples); - if (unlikely (!var.indices.resize (point_count) || - !var.deltas_x.resize (point_count, false))) + if (unlikely (!var.indices.allocate_from_pool (pool, point_count) || + !var.deltas_x.allocate_from_pool (pool, point_count, false))) return false; - if (is_gvar && unlikely (!var.deltas_y.resize (point_count, false))) + if (is_gvar && unlikely (!var.deltas_y.allocate_from_pool (pool, point_count, false))) return false; for (unsigned i = 0; i < num_deltas; i++) @@ -1012,8 +1088,8 @@ struct TupleVariationData /* In VarData, deltas are organized in rows, convert them into * column(region) based tuples, resize deltas_x first */ tuple_delta_t tuple; - if (!tuple.deltas_x.resize (item_count, false) || - !tuple.indices.resize (item_count, false)) + if (!tuple.deltas_x.resize_dirty (item_count) || + !tuple.indices.resize_dirty (item_count)) return false; for (unsigned i = 0; i < item_count; i++) @@ -1041,7 +1117,8 @@ struct TupleVariationData } bool change_tuple_variations_axis_limits (const hb_hashmap_t& normalized_axes_location, - const hb_hashmap_t& axes_triple_distances) + const hb_hashmap_t& axes_triple_distances, + hb_alloc_pool_t *pool = nullptr) { /* sort axis_tag/axis_limits, make result deterministic */ hb_vector_t axis_tags; @@ -1050,6 +1127,10 @@ struct TupleVariationData for (auto t : normalized_axes_location.keys ()) axis_tags.push (t); + // Reused vectors for reduced malloc pressure. + rebase_tent_result_scratch_t scratch; + hb_vector_t out; + axis_tags.qsort (_cmp_axis_tag); for (auto axis_tag : axis_tags) { @@ -1061,9 +1142,10 @@ struct TupleVariationData axis_triple_distances = axes_triple_distances.get (axis_tag); hb_vector_t new_vars; - for (const tuple_delta_t& var : tuple_vars) + for (tuple_delta_t& var : tuple_vars) { - hb_vector_t out = var.change_tuple_var_axis_limit (axis_tag, *axis_limit, axis_triple_distances); + // This may move var out. + var.change_tuple_var_axis_limit (axis_tag, *axis_limit, axis_triple_distances, out, scratch, pool); if (!out) continue; unsigned new_len = new_vars.length + out.length; @@ -1074,7 +1156,6 @@ struct TupleVariationData for (unsigned i = 0; i < out.length; i++) new_vars.push (std::move (out[i])); } - tuple_vars.fini (); tuple_vars = std::move (new_vars); } return true; @@ -1085,9 +1166,12 @@ struct TupleVariationData bool merge_tuple_variations (contour_point_vector_t* contour_points = nullptr) { hb_vector_t new_vars; + // The pre-allocation is essential for address stability of pointers + // we store in the hashmap. + if (unlikely (!new_vars.alloc (tuple_vars.length))) + return false; hb_hashmap_t*, unsigned> m; - unsigned i = 0; - for (const tuple_delta_t& var : tuple_vars) + for (tuple_delta_t& var : tuple_vars) { /* if all axes are pinned, drop the tuple variation */ if (var.axis_tuples.is_empty ()) @@ -1107,13 +1191,17 @@ struct TupleVariationData } else { - new_vars.push (var); - if (!m.set (&(var.axis_tuples), i)) + auto *new_var = new_vars.push (); + if (unlikely (new_vars.in_error ())) + return false; + hb_swap (*new_var, var); + if (unlikely (!m.set (&(new_var->axis_tuples), new_vars.length - 1))) return false; - i++; } } - tuple_vars.fini (); + m.fini (); // Just in case, since it points into new_vars data. + // Shouldn't be necessary though, since we only move new_vars, not its + // contents. tuple_vars = std::move (new_vars); return true; } @@ -1173,20 +1261,22 @@ struct TupleVariationData } } - bool calc_inferred_deltas (const contour_point_vector_t& contour_points) + bool calc_inferred_deltas (const contour_point_vector_t& contour_points, + hb_vector_t &scratch) { for (tuple_delta_t& var : tuple_vars) - if (!var.calc_inferred_deltas (contour_points)) + if (!var.calc_inferred_deltas (contour_points, scratch)) return false; return true; } - bool iup_optimize (const contour_point_vector_t& contour_points) + bool iup_optimize (const contour_point_vector_t& contour_points, + optimize_scratch_t &scratch) { for (tuple_delta_t& var : tuple_vars) { - if (!var.optimize (contour_points, is_composite)) + if (!var.optimize (contour_points, is_composite, scratch)) return false; } return true; @@ -1195,16 +1285,21 @@ struct TupleVariationData public: bool instantiate (const hb_hashmap_t& normalized_axes_location, const hb_hashmap_t& axes_triple_distances, + optimize_scratch_t &scratch, + hb_alloc_pool_t *pool = nullptr, contour_point_vector_t* contour_points = nullptr, bool optimize = false) { if (!tuple_vars) return true; - if (!change_tuple_variations_axis_limits (normalized_axes_location, axes_triple_distances)) + if (!change_tuple_variations_axis_limits (normalized_axes_location, axes_triple_distances, pool)) return false; /* compute inferred deltas only for gvar */ if (contour_points) - if (!calc_inferred_deltas (*contour_points)) + { + hb_vector_t scratch; + if (!calc_inferred_deltas (*contour_points, scratch)) return false; + } /* if iup delta opt is on, contour_points can't be null */ if (optimize && !contour_points) @@ -1213,7 +1308,7 @@ struct TupleVariationData if (!merge_tuple_variations (optimize ? contour_points : nullptr)) return false; - if (optimize && !iup_optimize (*contour_points)) return false; + if (optimize && !iup_optimize (*contour_points, scratch)) return false; return !tuple_vars.in_error (); } @@ -1221,7 +1316,8 @@ struct TupleVariationData const hb_map_t& axes_old_index_tag_map, bool use_shared_points, bool is_gvar = false, - const hb_hashmap_t*, unsigned>* shared_tuples_idx_map = nullptr) + const hb_hashmap_t*, unsigned>* shared_tuples_idx_map = nullptr, + hb_alloc_pool_t *pool = nullptr) { // return true for empty glyph if (!tuple_vars) @@ -1241,6 +1337,7 @@ struct TupleVariationData if (shared_points_bytes) compiled_byte_size += shared_points_bytes->length; } + hb_vector_t rounded_deltas_scratch; // compile delta and tuple var header for each tuple variation for (auto& tuple: tuple_vars) { @@ -1254,12 +1351,13 @@ struct TupleVariationData * this tuple */ if (!points_data->length) continue; - if (!tuple.compile_deltas ()) + if (!tuple.compile_deltas (rounded_deltas_scratch, pool)) return false; unsigned points_data_length = (points_data != shared_points_bytes) ? points_data->length : 0; if (!tuple.compile_tuple_var_header (axes_index_map, points_data_length, axes_old_index_tag_map, - shared_tuples_idx_map)) + shared_tuples_idx_map, + pool)) return false; compiled_byte_size += tuple.compiled_tuple_header.length + points_data_length + tuple.compiled_deltas.length; } @@ -1330,8 +1428,9 @@ struct TupleVariationData { var_data_bytes = var_data_bytes_; var_data = var_data_bytes_.as (); - index = 0; + tuples_left = var_data->tupleVarCount.get_count (); axis_count = axis_count_; + axis_count_times_2 = axis_count_ * 2; current_tuple = &var_data->get_tuple_var_header (); data_offset = 0; table_base = table_base_; @@ -1349,30 +1448,42 @@ struct TupleVariationData return true; } - bool is_valid () const + bool is_valid () { - return (index < var_data->tupleVarCount.get_count ()) && - var_data_bytes.check_range (current_tuple, TupleVariationHeader::min_size) && - var_data_bytes.check_range (current_tuple, hb_max (current_tuple->get_data_size (), - current_tuple->get_size (axis_count))); + if (unlikely (tuples_left <= 0)) + return false; + + current_tuple_size = TupleVariationHeader::min_size; + if (unlikely (!var_data_bytes.check_end ((const char *) current_tuple + current_tuple_size))) + return false; + + current_tuple_size = current_tuple->get_size (axis_count_times_2); + if (unlikely (!var_data_bytes.check_end ((const char *) current_tuple + current_tuple_size))) + return false; + + return true; } + HB_ALWAYS_INLINE bool move_to_next () { data_offset += current_tuple->get_data_size (); - current_tuple = ¤t_tuple->get_next (axis_count); - index++; + current_tuple = &StructAtOffset (current_tuple, current_tuple_size); + tuples_left--; return is_valid (); } + // TODO: Make it return (sanitized) hb_bytes_t const HBUINT8 *get_serialized_data () const { return &(table_base+var_data->data) + data_offset; } private: + signed tuples_left; const TupleVariationData *var_data; - unsigned int index; unsigned int axis_count; + unsigned int axis_count_times_2; unsigned int data_offset; + unsigned int current_tuple_size; const void *table_base; public: @@ -1411,7 +1522,7 @@ struct TupleVariationData if (unlikely (p + 1 > end)) return false; count = ((count & POINT_RUN_COUNT_MASK) << 8) | *p++; } - if (unlikely (!points.resize (count, false))) return false; + if (unlikely (!points.resize_dirty (count))) return false; unsigned n = 0; unsigned i = 0; @@ -1446,12 +1557,14 @@ struct TupleVariationData } template + HB_ALWAYS_INLINE static bool decompile_deltas (const HBUINT8 *&p /* IN/OUT */, hb_vector_t &deltas /* IN/OUT */, const HBUINT8 *end, - bool consume_all = false) + bool consume_all = false, + unsigned start = 0) { - return TupleValues::decompile (p, deltas, end, consume_all); + return TupleValues::decompile (p, deltas, end, consume_all, start); } bool has_data () const { return tupleVarCount; } @@ -1463,6 +1576,7 @@ struct TupleVariationData const hb_vector_t &shared_indices, const hb_array_t shared_tuples, tuple_variations_t& tuple_variations, /* OUT */ + hb_alloc_pool_t *pool = nullptr, bool is_composite_glyph = false) const { return tuple_variations.create_from_tuple_var_data (iterator, tupleVarCount, @@ -1470,6 +1584,7 @@ struct TupleVariationData axes_old_index_tag_map, shared_indices, shared_tuples, + pool, is_composite_glyph); } @@ -1634,8 +1749,9 @@ struct item_variations_t bool instantiate_tuple_vars (const hb_hashmap_t& normalized_axes_location, const hb_hashmap_t& axes_triple_distances) { + optimize_scratch_t scratch; for (tuple_variations_t& tuple_vars : vars) - if (!tuple_vars.instantiate (normalized_axes_location, axes_triple_distances)) + if (!tuple_vars.instantiate (normalized_axes_location, axes_triple_distances, scratch)) return false; if (!build_region_list ()) return false; @@ -1720,30 +1836,28 @@ struct item_variations_t struct combined_gain_idx_tuple_t { - int gain; - unsigned idx_1; - unsigned idx_2; + uint64_t encoded; combined_gain_idx_tuple_t () = default; - combined_gain_idx_tuple_t (int gain_, unsigned i, unsigned j) - :gain (gain_), idx_1 (i), idx_2 (j) {} + combined_gain_idx_tuple_t (unsigned gain, unsigned i, unsigned j) + : encoded ((uint64_t (0xFFFFFF - gain) << 40) | (uint64_t (i) << 20) | uint64_t (j)) + { + assert (gain < 0xFFFFFF); + assert (i < 0xFFFFFFF && j < 0xFFFFFFF); + } bool operator < (const combined_gain_idx_tuple_t& o) { - if (gain != o.gain) - return gain < o.gain; - - if (idx_1 != o.idx_1) - return idx_1 < o.idx_1; - - return idx_2 < o.idx_2; + return encoded < o.encoded; } bool operator <= (const combined_gain_idx_tuple_t& o) { - if (*this < o) return true; - return gain == o.gain && idx_1 == o.idx_1 && idx_2 == o.idx_2; + return encoded <= o.encoded; } + + unsigned idx_1 () const { return (encoded >> 20) & 0xFFFFF; }; + unsigned idx_2 () const { return encoded & 0xFFFFF; }; }; bool as_item_varstore (bool optimize=true, bool use_no_variation_idx=true) @@ -1765,9 +1879,9 @@ struct item_variations_t hb_hashmap_t*> front_mapping; unsigned start_row = 0; hb_vector_t encoding_objs; - hb_hashmap_t, unsigned> chars_idx_map; /* delta_rows map, used for filtering out duplicate rows */ + hb_vector_t *> major_rows; hb_hashmap_t*, unsigned> delta_rows_map; for (unsigned major = 0; major < vars.length; major++) { @@ -1775,6 +1889,9 @@ struct item_variations_t * (row based) delta */ const tuple_variations_t& tuples = vars[major]; unsigned num_rows = var_data_num_rows[major]; + + if (!num_rows) continue; + for (const tuple_delta_t& tuple: tuples.tuple_vars) { if (tuple.deltas_x.length != num_rows) @@ -1789,24 +1906,11 @@ struct item_variations_t { int rounded_delta = roundf (tuple.deltas_x[i]); delta_rows[start_row + i][*col_idx] += rounded_delta; - if ((!has_long) && (rounded_delta < -65536 || rounded_delta > 65535)) - has_long = true; + has_long |= rounded_delta < -65536 || rounded_delta > 65535; } } - if (!optimize) - { - /* assemble a delta_row_encoding_t for this subtable, skip optimization so - * chars is not initialized, we only need delta rows for serialization */ - delta_row_encoding_t obj; - for (unsigned r = start_row; r < start_row + num_rows; r++) - obj.add_row (&(delta_rows.arrayZ[r])); - - encodings.push (std::move (obj)); - start_row += num_rows; - continue; - } - + major_rows.reset (); for (unsigned minor = 0; minor < num_rows; minor++) { const hb_vector_t& row = delta_rows[start_row + minor]; @@ -1828,42 +1932,40 @@ struct item_variations_t if (!front_mapping.set ((major<<16) + minor, &row)) return false; - hb_vector_t chars = delta_row_encoding_t::get_row_chars (row); - if (!chars) return false; - if (delta_rows_map.has (&row)) continue; delta_rows_map.set (&row, 1); - unsigned *obj_idx; - if (chars_idx_map.has (chars, &obj_idx)) - { - delta_row_encoding_t& obj = encoding_objs[*obj_idx]; - if (!obj.add_row (&row)) - return false; - } - else - { - if (!chars_idx_map.set (chars, encoding_objs.length)) - return false; - delta_row_encoding_t obj (std::move (chars), &row); - encoding_objs.push (std::move (obj)); - } + + major_rows.push (&row); } + if (major_rows) + encoding_objs.push (delta_row_encoding_t (std::move (major_rows), num_cols)); + start_row += num_rows; } /* return directly if no optimization, maintain original VariationIndex so * varidx_map would be empty */ - if (!optimize) return !encodings.in_error (); + if (!optimize) + { + encodings = std::move (encoding_objs); + return !encodings.in_error (); + } - /* sort encoding_objs */ + /* NOTE: Fonttools instancer always optimizes VarStore from scratch. This + * is too costly for large fonts. So, instead, we retain the encodings of + * the original VarStore, and just try to combine them if possible. This + * is a compromise between optimization and performance and practically + * works very well. */ + + // This produces slightly smaller results in some cases. encoding_objs.qsort (); - /* main algorithm: repeatedly pick 2 best encodings to combine, and combine - * them */ - hb_priority_queue_t queue; + /* main algorithm: repeatedly pick 2 best encodings to combine, and combine them */ + using item_t = hb_priority_queue_t::item_t; + hb_vector_t queue_items; unsigned num_todos = encoding_objs.length; for (unsigned i = 0; i < num_todos; i++) { @@ -1871,16 +1973,18 @@ struct item_variations_t { int combining_gain = encoding_objs.arrayZ[i].gain_from_merging (encoding_objs.arrayZ[j]); if (combining_gain > 0) - queue.insert (combined_gain_idx_tuple_t (-combining_gain, i, j), 0); + queue_items.push (item_t (combined_gain_idx_tuple_t (combining_gain, i, j), 0)); } } - hb_set_t removed_todo_idxes; + hb_priority_queue_t queue (std::move (queue_items)); + + hb_bit_set_t removed_todo_idxes; while (queue) { auto t = queue.pop_minimum ().first; - unsigned i = t.idx_1; - unsigned j = t.idx_2; + unsigned i = t.idx_1 (); + unsigned j = t.idx_2 (); if (removed_todo_idxes.has (i) || removed_todo_idxes.has (j)) continue; @@ -1891,40 +1995,36 @@ struct item_variations_t removed_todo_idxes.add (i); removed_todo_idxes.add (j); - hb_vector_t combined_chars; - if (!combined_chars.alloc (encoding.chars.length)) - return false; - - for (unsigned idx = 0; idx < encoding.chars.length; idx++) - { - uint8_t v = hb_max (encoding.chars.arrayZ[idx], other_encoding.chars.arrayZ[idx]); - combined_chars.push (v); - } - - delta_row_encoding_t combined_encoding_obj (std::move (combined_chars)); - for (const auto& row : hb_concat (encoding.items, other_encoding.items)) - combined_encoding_obj.add_row (row); + encoding.merge (other_encoding); for (unsigned idx = 0; idx < encoding_objs.length; idx++) { if (removed_todo_idxes.has (idx)) continue; const delta_row_encoding_t& obj = encoding_objs.arrayZ[idx]; - if (obj.chars == combined_chars) + // In the unlikely event that the same encoding exists already, combine it. + if (obj.width == encoding.width && obj.chars == encoding.chars) { + // This is straight port from fonttools algorithm. I added this branch there + // because I thought it can happen. But looks like we never get in here in + // practice. I'm not confident enough to remove it though; in theory it can + // happen. I think it's just that our tests are not extensive enough to hit + // this path. + for (const auto& row : obj.items) - combined_encoding_obj.add_row (row); + encoding.add_row (row); removed_todo_idxes.add (idx); continue; } - int combined_gain = combined_encoding_obj.gain_from_merging (obj); + int combined_gain = encoding.gain_from_merging (obj); if (combined_gain > 0) - queue.insert (combined_gain_idx_tuple_t (-combined_gain, idx, encoding_objs.length), 0); + queue.insert (combined_gain_idx_tuple_t (combined_gain, idx, encoding_objs.length), 0); } - encoding_objs.push (std::move (combined_encoding_obj)); + auto moved_encoding = std::move (encoding); + encoding_objs.push (moved_encoding); } int num_final_encodings = (int) encoding_objs.length - (int) removed_todo_idxes.get_population (); @@ -1937,9 +2037,6 @@ struct item_variations_t encodings.push (std::move (encoding_objs.arrayZ[i])); } - /* sort again based on width, make result deterministic */ - encodings.qsort (delta_row_encoding_t::cmp_width); - return compile_varidx_map (front_mapping); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-cvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-cvar-table.hh index 3dc4ebaebd8..d8f497d5346 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-cvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-cvar-table.hh @@ -84,7 +84,7 @@ struct cvar if (!coords) return true; hb_vector_t shared_indices; TupleVariationData<>::tuple_iterator_t iterator; - unsigned var_data_length = tuple_var_data->get_size (axis_count); + unsigned var_data_length = tuple_var_data->get_size (axis_count * 2); hb_bytes_t var_data_bytes = hb_bytes_t (reinterpret_cast (tuple_var_data), var_data_length); if (!TupleVariationData<>::get_tuple_iterator (var_data_bytes, axis_count, base, shared_indices, &iterator)) @@ -113,7 +113,7 @@ struct cvar bool apply_to_all = (indices.length == 0); unsigned num_deltas = apply_to_all ? num_cvt_item : indices.length; - if (unlikely (!unpacked_deltas.resize (num_deltas, false))) return false; + if (unlikely (!unpacked_deltas.resize_dirty (num_deltas))) return false; if (unlikely (!TupleVariationData<>::decompile_deltas (p, unpacked_deltas, end))) return false; for (unsigned int i = 0; i < num_deltas; i++) @@ -158,7 +158,8 @@ struct cvar tuple_variations)) return_trace (false); - if (!tuple_variations.instantiate (c->plan->axes_location, c->plan->axes_triple_distances)) + optimize_scratch_t scratch; + if (!tuple_variations.instantiate (c->plan->axes_location, c->plan->axes_triple_distances, scratch)) return_trace (false); if (!tuple_variations.compile_bytes (c->plan->axes_index_map, c->plan->axes_old_index_tag_map, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh index f2725eaa282..74d392de9b7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh @@ -179,7 +179,7 @@ struct AxisRecord hb_tag_t get_axis_tag () const { return axisTag; } - int normalize_axis_value (float v) const + float normalize_axis_value (float v) const { float min_value, default_value, max_value; get_coordinates (min_value, default_value, max_value); @@ -189,23 +189,9 @@ struct AxisRecord if (v == default_value) return 0; else if (v < default_value) - v = (v - default_value) / (default_value - min_value); + return (v - default_value) / (default_value - min_value); else - v = (v - default_value) / (max_value - default_value); - return roundf (v * 16384.f); - } - - float unnormalize_axis_value (int v) const - { - float min_value, default_value, max_value; - get_coordinates (min_value, default_value, max_value); - - if (v == 0) - return default_value; - else if (v < 0) - return v * (default_value - min_value) / 16384.f + default_value; - else - return v * (max_value - default_value) / 16384.f + default_value; + return (v - default_value) / (max_value - default_value); } hb_ot_name_id_t get_name_id () const { return axisNameID; } @@ -341,12 +327,9 @@ struct fvar return axes.lfind (tag, &i) && ((void) axes[i].get_axis_info (i, info), true); } - int normalize_axis_value (unsigned int axis_index, float v) const + float normalize_axis_value (unsigned int axis_index, float v) const { return get_axes ()[axis_index].normalize_axis_value (v); } - float unnormalize_axis_value (unsigned int axis_index, int v) const - { return get_axes ()[axis_index].unnormalize_axis_value (v); } - unsigned int get_instance_count () const { return instanceCount; } hb_ot_name_id_t get_instance_subfamily_name_id (unsigned int instance_index) const diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh index 9f9b5be3cbe..7c42f548456 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh @@ -66,12 +66,14 @@ struct glyph_variations_t hb_vector_t glyph_variations; - hb_vector_t compiled_shared_tuples; + hb_vector_t compiled_shared_tuples; private: unsigned shared_tuples_count = 0; /* shared coords-> index map after instantiation */ - hb_hashmap_t*, unsigned> shared_tuples_idx_map; + hb_hashmap_t*, unsigned> shared_tuples_idx_map; + + hb_alloc_pool_t pool; public: unsigned compiled_shared_tuples_count () const @@ -128,6 +130,7 @@ struct glyph_variations_t iterator, &(plan->axes_old_index_tag_map), shared_indices, shared_tuples, tuple_vars, /* OUT */ + &pool, is_composite_glyph)) return false; glyph_variations.push (std::move (tuple_vars)); @@ -139,6 +142,7 @@ struct glyph_variations_t { unsigned count = plan->new_to_old_gid_list.length; bool iup_optimize = false; + optimize_scratch_t scratch; iup_optimize = plan->flags & HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS; for (unsigned i = 0; i < count; i++) { @@ -146,7 +150,7 @@ struct glyph_variations_t contour_point_vector_t *all_points; if (!plan->new_gid_contour_points_map.has (new_gid, &all_points)) return false; - if (!glyph_variations[i].instantiate (plan->axes_location, plan->axes_triple_distances, all_points, iup_optimize)) + if (!glyph_variations[i].instantiate (plan->axes_location, plan->axes_triple_distances, scratch, &pool, all_points, iup_optimize)) return false; } return true; @@ -161,7 +165,8 @@ struct glyph_variations_t if (!vars.compile_bytes (axes_index_map, axes_old_index_tag_map, true, /* use shared points*/ true, - &shared_tuples_idx_map)) + &shared_tuples_idx_map, + &pool)) return false; return true; @@ -172,20 +177,21 @@ struct glyph_variations_t { /* key is pointer to compiled_peak_coords inside each tuple, hashing * function will always deref pointers first */ - hb_hashmap_t*, unsigned> coords_count_map; + hb_hashmap_t*, unsigned> coords_count_map; /* count the num of shared coords */ for (tuple_variations_t& vars: glyph_variations) { for (tuple_delta_t& var : vars.tuple_vars) { - if (!var.compile_peak_coords (axes_index_map, axes_old_index_tag_map)) + if (!var.compile_coords (axes_index_map, axes_old_index_tag_map, &pool)) return false; - unsigned* count; - if (coords_count_map.has (&(var.compiled_peak_coords), &count)) - coords_count_map.set (&(var.compiled_peak_coords), *count + 1); + unsigned *count; + unsigned hash = hb_hash (&var.compiled_peak_coords); + if (coords_count_map.has_with_hash (&(var.compiled_peak_coords), hash, &count)) + (*count)++; else - coords_count_map.set (&(var.compiled_peak_coords), 1); + coords_count_map.set_with_hash (&(var.compiled_peak_coords), hash, 1); } } @@ -193,66 +199,45 @@ struct glyph_variations_t return false; /* add only those coords that are used more than once into the vector and sort */ - hb_vector_t*> shared_coords; - if (unlikely (!shared_coords.alloc (coords_count_map.get_population ()))) - return false; - - for (const auto _ : coords_count_map.iter ()) - { - if (_.second == 1) continue; - shared_coords.push (_.first); - } + hb_vector_t*, unsigned>> shared_coords { + + hb_iter (coords_count_map) + | hb_filter ([] (const hb_pair_t*, unsigned>& p) { return p.second > 1; }) + }; + if (unlikely (shared_coords.in_error ())) return false; /* no shared tuples: no coords are used more than once */ if (!shared_coords) return true; /* sorting based on the coords frequency first (high to low), then compare * the coords bytes */ - hb_qsort (shared_coords.arrayZ, shared_coords.length, sizeof (hb_vector_t*), _cmp_coords, (void *) (&coords_count_map)); + shared_coords.qsort (_cmp_coords); /* build shared_coords->idx map and shared tuples byte array */ shared_tuples_count = hb_min (0xFFFu + 1, shared_coords.length); - unsigned len = shared_tuples_count * (shared_coords[0]->length); + unsigned len = shared_tuples_count * (shared_coords[0].first->length); if (unlikely (!compiled_shared_tuples.alloc (len))) return false; for (unsigned i = 0; i < shared_tuples_count; i++) { - shared_tuples_idx_map.set (shared_coords[i], i); + shared_tuples_idx_map.set (shared_coords[i].first, i); /* add a concat() in hb_vector_t? */ - for (char c : shared_coords[i]->iter ()) + for (auto c : shared_coords[i].first->iter ()) compiled_shared_tuples.push (c); } return true; } - static int _cmp_coords (const void *pa, const void *pb, void *arg) + static int _cmp_coords (const void *pa, const void *pb) { - const hb_hashmap_t*, unsigned>* coords_count_map = - reinterpret_cast*, unsigned>*> (arg); + const hb_pair_t *, unsigned> *a = (const hb_pair_t *, unsigned> *) pa; + const hb_pair_t *, unsigned> *b = (const hb_pair_t *, unsigned> *) pb; - /* shared_coords is hb_vector_t*> so casting pa/pb - * to be a pointer to a pointer */ - const hb_vector_t** a = reinterpret_cast**> (const_cast(pa)); - const hb_vector_t** b = reinterpret_cast**> (const_cast(pb)); + if (a->second != b->second) + return b->second - a->second; // high to low - bool has_a = coords_count_map->has (*a); - bool has_b = coords_count_map->has (*b); - - if (has_a && has_b) - { - unsigned a_num = coords_count_map->get (*a); - unsigned b_num = coords_count_map->get (*b); - - if (a_num != b_num) - return b_num - a_num; - - return (*b)->as_array().cmp ((*a)->as_array ()); - } - else if (has_a) return -1; - else if (has_b) return 1; - else return 0; + return b->first->as_array().cmp (a->first->as_array ()); } templatesharedTuples = 0; else { - hb_array_t shared_tuples = glyph_vars.compiled_shared_tuples.as_array ().copy (c); + hb_array_t shared_tuples = glyph_vars.compiled_shared_tuples.as_array ().copy (c); if (!shared_tuples.arrayZ) return_trace (false); - out->sharedTuples = shared_tuples.arrayZ - (char *) out; + out->sharedTuples = (const char *) shared_tuples.arrayZ - (char *) out; } char *glyph_var_data = c->start_embed (); @@ -463,10 +448,18 @@ struct gvar_GVAR if (it->first == 0 && !(c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) it++; unsigned int subset_data_size = 0; + unsigned padding_size = 0; for (auto &_ : it) { hb_codepoint_t old_gid = _.second; - subset_data_size += get_glyph_var_data_bytes (c->source_blob, glyph_count, old_gid).length; + unsigned glyph_data_size = get_glyph_var_data_bytes (c->source_blob, glyph_count, old_gid).length; + if (glyph_data_size % 2) + { + glyph_data_size++; + padding_size++; + } + + subset_data_size += glyph_data_size; } /* According to the spec: If the short format (Offset16) is used for offsets, @@ -495,6 +488,8 @@ struct gvar_GVAR /* This ordering relative to the shared tuples array, which puts the glyphVariationData last in the table, is required when HB_SUBSET_FLAGS_IFTB_REQUIREMENTS is set */ + if (long_offset) + subset_data_size -= padding_size; char *subset_data = c->serializer->allocate_size (subset_data_size, false); if (!subset_data) return_trace (false); out->dataZ = subset_data - (char *) out; @@ -533,8 +528,16 @@ struct gvar_GVAR old_gid); hb_memcpy (subset_data, var_data_bytes.arrayZ, var_data_bytes.length); - subset_data += var_data_bytes.length; - glyph_offset += var_data_bytes.length; + unsigned glyph_data_size = var_data_bytes.length; + subset_data += glyph_data_size; + glyph_offset += glyph_data_size; + + if (!long_offset && (glyph_data_size % 2)) + { + *subset_data = 0; + subset_data++; + glyph_offset++; + } if (long_offset) ((HBUINT32 *) subset_offsets)[gid] = glyph_offset; @@ -582,6 +585,17 @@ struct gvar_GVAR public: struct accelerator_t { + + hb_scalar_cache_t *create_cache () const + { + return hb_scalar_cache_t::create (table->sharedTupleCount); + } + + static void destroy_cache (hb_scalar_cache_t *cache) + { + hb_scalar_cache_t::destroy (cache); + } + bool has_data () const { return table->has_data (); } accelerator_t (hb_face_t *face) @@ -589,36 +603,6 @@ struct gvar_GVAR table = hb_sanitize_context_t ().reference_table (face); /* If sanitize failed, set glyphCount to 0. */ glyphCount = table->version.to_int () ? face->get_num_glyphs () : 0; - - /* For shared tuples that only have one or two axes active, shared the index - * of that axis as a cache. This will speed up caclulate_scalar() a lot - * for fonts with lots of axes and many "monovar" or "duovar" tuples. */ - hb_array_t shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * table->axisCount); - unsigned count = table->sharedTupleCount; - if (unlikely (!shared_tuple_active_idx.resize (count, false))) return; - unsigned axis_count = table->axisCount; - for (unsigned i = 0; i < count; i++) - { - hb_array_t tuple = shared_tuples.sub_array (axis_count * i, axis_count); - int idx1 = -1, idx2 = -1; - for (unsigned j = 0; j < axis_count; j++) - { - const F2DOT14 &peak = tuple.arrayZ[j]; - if (peak.to_int () != 0) - { - if (idx1 == -1) - idx1 = j; - else if (idx2 == -1) - idx2 = j; - else - { - idx1 = idx2 = -1; - break; - } - } - } - shared_tuple_active_idx.arrayZ[i] = {idx1, idx2}; - } } ~accelerator_t () { table.destroy (); } @@ -655,6 +639,7 @@ struct gvar_GVAR hb_array_t coords, const hb_array_t points, hb_glyf_scratch_t &scratch, + hb_scalar_cache_t *gvar_cache = nullptr, bool phantom_only = false) const { if (unlikely (glyph >= glyphCount)) return true; @@ -690,10 +675,12 @@ struct gvar_GVAR unsigned count = points.length; bool flush = false; + do { float scalar = iterator.current_tuple->calculate_scalar (coords, num_coords, shared_tuples, - &shared_tuple_active_idx); + gvar_cache); + if (scalar == 0.f) continue; const HBUINT8 *p = iterator.get_serialized_data (); unsigned int length = iterator.current_tuple->get_data_size (); @@ -702,7 +689,7 @@ struct gvar_GVAR if (!deltas) { - if (unlikely (!deltas_vec.resize (count, false))) return false; + if (unlikely (!deltas_vec.resize_dirty (count))) return false; deltas = deltas_vec.as_array (); hb_memset (deltas.arrayZ + (phantom_only ? count - 4 : 0), 0, (phantom_only ? 4 : count) * sizeof (deltas[0])); @@ -717,11 +704,12 @@ struct gvar_GVAR const hb_array_t &indices = has_private_points ? private_indices : shared_indices; bool apply_to_all = (indices.length == 0); - unsigned int num_deltas = apply_to_all ? points.length : indices.length; - if (unlikely (!x_deltas.resize (num_deltas, false))) return false; - if (unlikely (!GlyphVariationData::decompile_deltas (p, x_deltas, end))) return false; - if (unlikely (!y_deltas.resize (num_deltas, false))) return false; - if (unlikely (!GlyphVariationData::decompile_deltas (p, y_deltas, end))) return false; + unsigned num_deltas = apply_to_all ? points.length : indices.length; + unsigned start_deltas = (apply_to_all && phantom_only && num_deltas >= 4 ? num_deltas - 4 : 0); + if (unlikely (!x_deltas.resize_dirty (num_deltas))) return false; + if (unlikely (!GlyphVariationData::decompile_deltas (p, x_deltas, end, false, start_deltas))) return false; + if (unlikely (!y_deltas.resize_dirty (num_deltas))) return false; + if (unlikely (!GlyphVariationData::decompile_deltas (p, y_deltas, end, false, start_deltas))) return false; if (!apply_to_all) { @@ -884,7 +872,6 @@ struct gvar_GVAR private: hb_blob_ptr_t table; unsigned glyphCount; - hb_vector_t> shared_tuple_active_idx; }; protected: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh index 40a85a62b78..652d738e7b5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh @@ -42,57 +42,62 @@ struct index_map_subset_plan_t VORG_INDEX }; - void init (const DeltaSetIndexMap &index_map, + void init (const DeltaSetIndexMap *index_map, hb_inc_bimap_t &outer_map, hb_vector_t &inner_sets, const hb_subset_plan_t *plan, bool bypass_empty = true) { map_count = 0; - outer_bit_count = 0; - inner_bit_count = 1; max_inners.init (); output_map.init (); - if (bypass_empty && !index_map.get_map_count ()) return; + if (bypass_empty && (!index_map || !index_map->get_map_count ())) return; unsigned int last_val = (unsigned int)-1; hb_codepoint_t last_gid = HB_CODEPOINT_INVALID; - outer_bit_count = (index_map.get_width () * 8) - index_map.get_inner_bit_count (); max_inners.resize (inner_sets.length); for (unsigned i = 0; i < inner_sets.length; i++) max_inners[i] = 0; /* Search backwards for a map value different from the last map value */ auto &new_to_old_gid_list = plan->new_to_old_gid_list; unsigned count = new_to_old_gid_list.length; - for (unsigned j = count; j; j--) + if (!index_map) { - hb_codepoint_t gid = new_to_old_gid_list.arrayZ[j - 1].first; - hb_codepoint_t old_gid = new_to_old_gid_list.arrayZ[j - 1].second; - - unsigned int v = index_map.map (old_gid); - if (last_gid == HB_CODEPOINT_INVALID) + map_count = new_to_old_gid_list.tail ().first + 1; + } + else + { + for (unsigned j = count; j; j--) { + hb_codepoint_t gid = new_to_old_gid_list.arrayZ[j - 1].first; + hb_codepoint_t old_gid = new_to_old_gid_list.arrayZ[j - 1].second; + + unsigned int v = index_map->map (old_gid); + if (last_gid == HB_CODEPOINT_INVALID) + { last_val = v; last_gid = gid; continue; - } - if (v != last_val) + } + if (v != last_val) break; - last_gid = gid; + last_gid = gid; + } + + if (unlikely (last_gid == (hb_codepoint_t)-1)) return; + map_count = last_gid + 1; } - if (unlikely (last_gid == (hb_codepoint_t)-1)) return; - map_count = last_gid + 1; for (auto _ : plan->new_to_old_gid_list) { hb_codepoint_t gid = _.first; if (gid >= map_count) break; hb_codepoint_t old_gid = _.second; - unsigned int v = index_map.map (old_gid); + unsigned int v = index_map ? index_map->map (old_gid): old_gid; unsigned int outer = v >> 16; unsigned int inner = v & 0xFFFF; outer_map.add (outer); @@ -113,6 +118,9 @@ struct index_map_subset_plan_t const hb_vector_t &inner_maps, const hb_subset_plan_t *plan) { + outer_bit_count = 1; + inner_bit_count = 1; + for (unsigned int i = 0; i < max_inners.length; i++) { if (inner_maps[i].get_population () == 0) continue; @@ -128,9 +136,13 @@ struct index_map_subset_plan_t if (unlikely (new_gid >= map_count)) break; - uint32_t v = input_map->map (old_gid); - unsigned int outer = v >> 16; - output_map.arrayZ[new_gid] = (outer_map[outer] << 16) | (inner_maps[outer][v & 0xFFFF]); + uint32_t v = input_map? input_map->map (old_gid) : old_gid; + unsigned outer = v >> 16; + unsigned new_outer = outer_map[outer]; + unsigned bit_count = (new_outer == 0) ? 1 : hb_bit_storage (new_outer); + outer_bit_count = hb_max (bit_count, outer_bit_count); + + output_map.arrayZ[new_gid] = (new_outer << 16) | (inner_maps[outer][v & 0xFFFF]); } } @@ -204,8 +216,8 @@ struct hvarvvar_subset_plan_t if (unlikely (!index_map_plans.length || !inner_sets.length || !inner_maps.length)) return; bool retain_adv_map = false; - index_map_plans[0].init (*index_maps[0], outer_map, inner_sets, plan, false); - if (index_maps[0] == &Null (DeltaSetIndexMap)) + index_map_plans[0].init (index_maps[0], outer_map, inner_sets, plan, false); + if (!index_maps[0]) { retain_adv_map = plan->flags & HB_SUBSET_FLAGS_RETAIN_GIDS; outer_map.add (0); @@ -215,7 +227,7 @@ struct hvarvvar_subset_plan_t } for (unsigned int i = 1; i < index_maps.length; i++) - index_map_plans[i].init (*index_maps[i], outer_map, inner_sets, plan); + index_map_plans[i].init (index_maps[i], outer_map, inner_sets, plan); outer_map.sort (); @@ -284,6 +296,8 @@ struct HVARVVAR static constexpr hb_tag_t HVARTag = HB_OT_TAG_HVAR; static constexpr hb_tag_t VVARTag = HB_OT_TAG_VVAR; + bool has_data () const { return version.major != 0; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -301,9 +315,14 @@ struct HVARVVAR void listup_index_maps (hb_vector_t &index_maps) const { - index_maps.push (&(this+advMap)); - index_maps.push (&(this+lsbMap)); - index_maps.push (&(this+rsbMap)); + if (advMap) index_maps.push (&(this+advMap)); + else index_maps.push (nullptr); + + if (lsbMap) index_maps.push (&(this+lsbMap)); + else index_maps.push (nullptr); + + if (rsbMap) index_maps.push (&(this+rsbMap)); + else index_maps.push (nullptr); } bool serialize_index_maps (hb_serialize_context_t *c, @@ -382,9 +401,10 @@ struct HVARVVAR hvar_plan.index_map_plans.as_array ())); } + HB_ALWAYS_INLINE float get_advance_delta_unscaled (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, - ItemVariationStore::cache_t *store_cache = nullptr) const + hb_scalar_cache_t *store_cache = nullptr) const { uint32_t varidx = (this+advMap).map (glyph); return (this+varStore).get_delta (varidx, @@ -392,16 +412,6 @@ struct HVARVVAR store_cache); } - bool get_lsb_delta_unscaled (hb_codepoint_t glyph, - const int *coords, unsigned int coord_count, - float *lsb) const - { - if (!lsbMap) return false; - uint32_t varidx = (this+lsbMap).map (glyph); - *lsb = (this+varStore).get_delta (varidx, coords, coord_count); - return true; - } - public: FixedVersion<>version; /* Version of the metrics variation table * initially set to 0x00010000u */ @@ -435,7 +445,8 @@ struct VVAR : HVARVVAR { void listup_index_maps (hb_vector_t &index_maps) const { HVARVVAR::listup_index_maps (index_maps); - index_maps.push (&(this+vorgMap)); + if (vorgMap) index_maps.push (&(this+vorgMap)); + else index_maps.push (nullptr); } bool serialize_index_maps (hb_serialize_context_t *c, @@ -454,14 +465,16 @@ struct VVAR : HVARVVAR { bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset (c); } - bool get_vorg_delta_unscaled (hb_codepoint_t glyph, - const int *coords, unsigned int coord_count, - float *delta) const + HB_ALWAYS_INLINE + float get_vorg_delta_unscaled (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + hb_scalar_cache_t *store_cache = nullptr) const { - if (!vorgMap) return false; + if (!vorgMap) return 0.f; uint32_t varidx = (this+vorgMap).map (glyph); - *delta = (this+varStore).get_delta (varidx, coords, coord_count); - return true; + return (this+varStore).get_delta (varidx, + coords, coord_count, + store_cache); } protected: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc index 8c695c41e24..4d594d587d8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc @@ -286,10 +286,14 @@ hb_ot_var_normalize_variations (hb_face_t *face, hb_ot_var_axis_info_t info; if (hb_ot_var_find_axis_info (face, variations[i].tag, &info) && info.axis_index < coords_length) - coords[info.axis_index] = fvar.normalize_axis_value (info.axis_index, variations[i].value); + coords[info.axis_index] = roundf (fvar.normalize_axis_value (info.axis_index, variations[i].value) * 65536.0f); } - face->table.avar->map_coords (coords, coords_length); + face->table.avar->map_coords_16_16 (coords, coords_length); + + // Round to 2.14 + for (unsigned i = 0; i < coords_length; i++) + coords[i] = (coords[i] + 2) >> 2; } /** @@ -309,6 +313,10 @@ hb_ot_var_normalize_variations (hb_face_t *face, * Any additional scaling defined in the face's `avar` table is also * applied, as described at https://docs.microsoft.com/en-us/typography/opentype/spec/avar * + * Note: @coords_length must be the same as the number of axes in the face, as + * for example returned by hb_ot_var_get_axis_count(). + * Otherwise, the behavior is undefined. + * * Since: 1.4.2 **/ void @@ -319,9 +327,13 @@ hb_ot_var_normalize_coords (hb_face_t *face, { const OT::fvar &fvar = *face->table.fvar; for (unsigned int i = 0; i < coords_length; i++) - normalized_coords[i] = fvar.normalize_axis_value (i, design_coords[i]); + normalized_coords[i] = roundf (fvar.normalize_axis_value (i, design_coords[i]) * 65536.0f); - face->table.avar->map_coords (normalized_coords, coords_length); + face->table.avar->map_coords_16_16 (normalized_coords, coords_length); + + // Round to 2.14 + for (unsigned i = 0; i < coords_length; i++) + normalized_coords[i] = (normalized_coords[i] + 2) >> 2; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh index b2300a327da..ff7ce678e4e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh @@ -61,6 +61,7 @@ struct VORG bool has_data () const { return version.to_int (); } + HB_ALWAYS_INLINE int get_y_origin (hb_codepoint_t glyph) const { unsigned int i; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-outline.cc b/src/java.desktop/share/native/libharfbuzz/hb-outline.cc index 85ffc793678..12963fe46e3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-outline.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-outline.cc @@ -84,6 +84,15 @@ void hb_outline_t::replay (hb_draw_funcs_t *pen, void *pen_data) const } } +void hb_outline_t::translate (float dx, float dy) +{ + for (auto &p : points) + { + p.x += dx; + p.y += dy; + } +} + void hb_outline_t::slant (float slant_xy) { for (auto &p : points) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-outline.hh b/src/java.desktop/share/native/libharfbuzz/hb-outline.hh index c5b68c05dd7..9e394a68d9c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-outline.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-outline.hh @@ -69,6 +69,7 @@ struct hb_outline_t HB_INTERNAL void replay (hb_draw_funcs_t *pen, void *pen_data) const; HB_INTERNAL float control_area () const; + HB_INTERNAL void translate (float dx, float dy); HB_INTERNAL void slant (float slant_xy); HB_INTERNAL void embolden (float x_strength, float y_strength, float x_shift, float y_shift); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc index bc08c5a9e14..e867f37f3c5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc @@ -49,7 +49,7 @@ hb_paint_extents_push_transform (hb_paint_funcs_t *funcs HB_UNUSED, { hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; - c->push_transform (hb_transform_t {xx, yx, xy, yy, dx, dy}); + c->push_transform (hb_transform_t<> {xx, yx, xy, yy, dx, dy}); } static void @@ -71,7 +71,7 @@ hb_paint_extents_push_clip_glyph (hb_paint_funcs_t *funcs HB_UNUSED, { hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; - hb_extents_t extents; + hb_extents_t<> extents; hb_draw_funcs_t *draw_extent_funcs = hb_draw_extents_get_funcs (); hb_font_draw_glyph (font, glyph, draw_extent_funcs, &extents); c->push_clip (extents); @@ -85,7 +85,7 @@ hb_paint_extents_push_clip_rectangle (hb_paint_funcs_t *funcs HB_UNUSED, { hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; - hb_extents_t extents = {xmin, ymin, xmax, ymax}; + hb_extents_t<> extents = {xmin, ymin, xmax, ymax}; c->push_clip (extents); } @@ -136,10 +136,10 @@ hb_paint_extents_paint_image (hb_paint_funcs_t *funcs HB_UNUSED, if (!glyph_extents) return false; // Happens with SVG images. - hb_extents_t extents = {(float) glyph_extents->x_bearing, - (float) glyph_extents->y_bearing + glyph_extents->height, - (float) glyph_extents->x_bearing + glyph_extents->width, - (float) glyph_extents->y_bearing}; + hb_extents_t<> extents = {(float) glyph_extents->x_bearing, + (float) glyph_extents->y_bearing + glyph_extents->height, + (float) glyph_extents->x_bearing + glyph_extents->width, + (float) glyph_extents->y_bearing}; c->push_clip (extents); c->paint (); c->pop_clip (); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh index 60374eaa2d2..531eb3c7c52 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh @@ -41,9 +41,9 @@ struct hb_paint_extents_context_t clips.clear (); groups.clear (); - transforms.push (hb_transform_t{}); - clips.push (hb_bounds_t{hb_bounds_t::UNBOUNDED}); - groups.push (hb_bounds_t{hb_bounds_t::EMPTY}); + transforms.push (hb_transform_t<>{}); + clips.push (hb_bounds_t<>{hb_bounds_t<>::UNBOUNDED}); + groups.push (hb_bounds_t<>{hb_bounds_t<>::EMPTY}); } hb_paint_extents_context_t () @@ -51,19 +51,19 @@ struct hb_paint_extents_context_t clear (); } - hb_extents_t get_extents () + hb_extents_t<> get_extents () { return groups.tail().extents; } bool is_bounded () { - return groups.tail().status != hb_bounds_t::UNBOUNDED; + return groups.tail().status != hb_bounds_t<>::UNBOUNDED; } - void push_transform (const hb_transform_t &trans) + void push_transform (const hb_transform_t<> &trans) { - hb_transform_t t = transforms.tail (); + hb_transform_t<> t = transforms.tail (); t.multiply (trans); transforms.push (t); } @@ -73,13 +73,13 @@ struct hb_paint_extents_context_t transforms.pop (); } - void push_clip (hb_extents_t extents) + void push_clip (hb_extents_t<> extents) { /* Transform extents and push a new clip. */ - const hb_transform_t &t = transforms.tail (); + const hb_transform_t<> &t = transforms.tail (); t.transform_extents (extents); - auto bounds = hb_bounds_t {extents}; + auto bounds = hb_bounds_t<> {extents}; bounds.intersect (clips.tail ()); clips.push (bounds); @@ -92,19 +92,19 @@ struct hb_paint_extents_context_t void push_group () { - groups.push (hb_bounds_t {hb_bounds_t::EMPTY}); + groups.push (hb_bounds_t<> {hb_bounds_t<>::EMPTY}); } void pop_group (hb_paint_composite_mode_t mode) { - const hb_bounds_t src_bounds = groups.pop (); - hb_bounds_t &backdrop_bounds = groups.tail (); + const hb_bounds_t<> src_bounds = groups.pop (); + hb_bounds_t<> &backdrop_bounds = groups.tail (); // https://learn.microsoft.com/en-us/typography/opentype/spec/colr#format-32-paintcomposite switch ((int) mode) { case HB_PAINT_COMPOSITE_MODE_CLEAR: - backdrop_bounds.status = hb_bounds_t::EMPTY; + backdrop_bounds.status = hb_bounds_t<>::EMPTY; break; case HB_PAINT_COMPOSITE_MODE_SRC: case HB_PAINT_COMPOSITE_MODE_SRC_OUT: @@ -125,16 +125,16 @@ struct hb_paint_extents_context_t void paint () { - const hb_bounds_t &clip = clips.tail (); - hb_bounds_t &group = groups.tail (); + const hb_bounds_t<> &clip = clips.tail (); + hb_bounds_t<> &group = groups.tail (); group.union_ (clip); } protected: - hb_vector_t transforms; - hb_vector_t clips; - hb_vector_t groups; + hb_vector_t> transforms; + hb_vector_t> clips; + hb_vector_t> groups; }; HB_INTERNAL hb_paint_funcs_t * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint.hh b/src/java.desktop/share/native/libharfbuzz/hb-paint.hh index 2abadebab3c..aedbcbdcbe2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint.hh @@ -28,6 +28,7 @@ #include "hb.hh" #include "hb-face.hh" #include "hb-font.hh" +#include "hb-geometry.hh" #define HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS \ HB_PAINT_FUNC_IMPLEMENT (push_transform) \ @@ -72,7 +73,11 @@ struct hb_paint_funcs_t float xx, float yx, float xy, float yy, float dx, float dy) - { func.push_transform (this, paint_data, + { + // Handle -0.f to avoid -0.f == 0.f in the transform matrix. + if (dx == -0.f) dx = 0.f; + if (dy == -0.f) dy = 0.f; + func.push_transform (this, paint_data, xx, yx, xy, yy, dx, dy, !user_data ? nullptr : user_data->push_transform); } void pop_transform (void *paint_data) @@ -182,54 +187,59 @@ struct hb_paint_funcs_t 0, 0); } - HB_NODISCARD - bool push_translate (void *paint_data, + void push_transform (void *paint_data, hb_transform_t t) + { + push_transform (paint_data, t.xx, t.yx, t.xy, t.yy, t.x0, t.y0); + } + + void push_translate (void *paint_data, float dx, float dy) { - if (!dx && !dy) - return false; - push_transform (paint_data, - 1.f, 0.f, 0.f, 1.f, dx, dy); - return true; + hb_transform_t::translation (dx, dy)); } - HB_NODISCARD - bool push_scale (void *paint_data, + void push_scale (void *paint_data, float sx, float sy) { - if (sx == 1.f && sy == 1.f) - return false; - push_transform (paint_data, - sx, 0.f, 0.f, sy, 0.f, 0.f); - return true; + hb_transform_t::scaling (sx, sy)); + } + void push_scale_around_center (void *paint_data, + float sx, float sy, + float cx, float cy) + { + push_transform (paint_data, + hb_transform_t::scaling_around_center (sx, sy, cx, cy)); } - HB_NODISCARD - bool push_rotate (void *paint_data, + void push_rotate (void *paint_data, float a) { - if (!a) - return false; - - float cc = cosf (a * HB_PI); - float ss = sinf (a * HB_PI); - push_transform (paint_data, cc, ss, -ss, cc, 0.f, 0.f); - return true; + push_transform (paint_data, + hb_transform_t::rotation (a * HB_PI)); } - HB_NODISCARD - bool push_skew (void *paint_data, + void push_rotate_around_center (void *paint_data, + float a, + float cx, float cy) + { + push_transform (paint_data, + hb_transform_t::rotation_around_center (a * HB_PI, cx, cy)); + } + + void push_skew (void *paint_data, float sx, float sy) { - if (!sx && !sy) - return false; - - float x = tanf (-sx * HB_PI); - float y = tanf (+sy * HB_PI); - push_transform (paint_data, 1.f, y, x, 1.f, 0.f, 0.f); - return true; + push_transform (paint_data, + hb_transform_t::skewing (-sx * HB_PI, sy * HB_PI)); + } + void push_skew_around_center (void *paint_data, + float sx, float sy, + float cx, float cy) + { + push_transform (paint_data, + hb_transform_t::skewing_around_center (-sx * HB_PI, sy * HB_PI, cx, cy)); } }; DECLARE_NULL_INSTANCE (hb_paint_funcs_t); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-priority-queue.hh b/src/java.desktop/share/native/libharfbuzz/hb-priority-queue.hh index 274d5df4c54..753e86b8ca6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-priority-queue.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-priority-queue.hh @@ -45,12 +45,22 @@ template struct hb_priority_queue_t { - private: + public: typedef hb_pair_t item_t; + + private: hb_vector_t heap; public: + hb_priority_queue_t () = default; + hb_priority_queue_t (hb_vector_t&& other) : heap (std::move (other)) + { + // Heapify the vector. + for (int i = (heap.length / 2) - 1; i >= 0; i--) + bubble_down (i); + } + void reset () { heap.resize (0); } bool in_error () const { return heap.in_error (); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-repacker.hh b/src/java.desktop/share/native/libharfbuzz/hb-repacker.hh index cb4fdeead2e..7d118b52169 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-repacker.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-repacker.hh @@ -217,11 +217,17 @@ bool _try_isolating_subgraphs (const hb_vector_t& over unsigned maximum_to_move = hb_max ((sorted_graph.num_roots_for_space (space) / 2u), 1u); if (roots_to_isolate.get_population () > maximum_to_move) { // Only move at most half of the roots in a space at a time. - unsigned extra = roots_to_isolate.get_population () - maximum_to_move; - while (extra--) { - uint32_t root = HB_SET_VALUE_INVALID; - roots_to_isolate.previous (&root); - roots_to_isolate.del (root); + // + // Note: this was ported from non-stable ids to stable ids. So to retain the same behaviour + // with regards to which roots are removed from the set we need to remove them in the topological + // order, not the object id order. + int extra = roots_to_isolate.get_population () - maximum_to_move; + for (unsigned id : sorted_graph.ordering_) { + if (!extra) break; + if (roots_to_isolate.has(id)) { + roots_to_isolate.del(id); + extra--; + } } } @@ -266,7 +272,7 @@ bool _resolve_shared_overflow(const hb_vector_t& overf result = sorted_graph.duplicate(&parents, r.child); } - if (result == (unsigned) -1) return result; + if (result == (unsigned) -1) return false; if (parents.get_population() > 1) { // If the duplicated node has more than one parent pre-emptively raise it's priority to the maximum. @@ -283,7 +289,7 @@ bool _resolve_shared_overflow(const hb_vector_t& overf sorted_graph.vertices_[result].give_max_priority(); } - return result; + return true; } static inline @@ -302,8 +308,11 @@ bool _process_overflows (const hb_vector_t& overflows, { // The child object is shared, we may be able to eliminate the overflow // by duplicating it. - if (!_resolve_shared_overflow(overflows, i, sorted_graph)) continue; - return true; + if (_resolve_shared_overflow(overflows, i, sorted_graph)) + return true; + + // Sometimes we can't duplicate a node which looks shared because it's not actually shared + // (eg. all links from the same parent) in this case continue on to other resolution options. } if (child.is_leaf () && !priority_bumped_parents.has (r.parent)) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh b/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh index 1f8ba32bae2..1e9ee9ba960 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh @@ -64,9 +64,6 @@ * * - Cast blob content to T*, call sanitize() method of it, * - If sanitize succeeded, return blob. - * - Otherwise, if blob is not writable, try making it writable, - * or copy if cannot be made writable in-place, - * - Call sanitize() again. Return blob if sanitize succeeded. * - Return empty blob otherwise. * * @@ -98,6 +95,12 @@ * structure is so complicated that by checking all offsets at sanitize() time, * we make the code much simpler in other methods, as offsets and referenced * objects do not need to be validated at each use site. + * + * Note: + * Sanitize was named so because it used to try to recover from errors by + * modifying the data to make it valid. This is no longer the case, as it + * could make HarfBuzz hallucinate new rules if there was aliasing in the + * data. However, the name stuck. See: https://behdad.github.io/harfbust/ */ /* This limits sanitizing time on really broken fonts. */ @@ -120,12 +123,12 @@ struct hb_sanitize_context_t : hb_dispatch_context_t { - hb_sanitize_context_t () : - start (nullptr), end (nullptr), + hb_sanitize_context_t (const char *start_ = nullptr, const char *end_ = nullptr) : + start (start_), end (end_), length (0), max_ops (0), max_subtables (0), recursion_depth (0), - writable (false), edit_count (0), + writable (false), blob (nullptr), num_glyphs (65536), num_glyphs_set (false), @@ -212,14 +215,22 @@ struct hb_sanitize_context_t : void reset_object () { - this->start = this->blob->data; - this->end = this->start + this->blob->length; + if (this->blob) + { + this->start = this->blob->data; + this->end = this->start + this->blob->length; + } this->length = this->end - this->start; assert (this->start <= this->end); /* Must not overflow. */ } - void start_processing () + void start_processing (const char *start_ = nullptr, const char *end_ = nullptr) { + if (start_) + { + this->start = start_; + this->end = end_; + } reset_object (); unsigned m; if (unlikely (hb_unsigned_mul_overflows (this->end - this->start, HB_SANITIZE_MAX_OPS_FACTOR, &m))) @@ -228,7 +239,6 @@ struct hb_sanitize_context_t : this->max_ops = hb_clamp (m, (unsigned) HB_SANITIZE_MAX_OPS_MIN, (unsigned) HB_SANITIZE_MAX_OPS_MAX); - this->edit_count = 0; this->debug_depth = 0; this->recursion_depth = 0; @@ -241,8 +251,8 @@ struct hb_sanitize_context_t : void end_processing () { DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1, - "end [%p..%p] %u edit requests", - this->start, this->end, this->edit_count); + "end [%p..%p]", + this->start, this->end); hb_blob_destroy (this->blob); this->blob = nullptr; @@ -250,9 +260,6 @@ struct hb_sanitize_context_t : this->length = 0; } - unsigned get_edit_count () { return edit_count; } - - bool check_ops(unsigned count) { /* Avoid underflow */ @@ -396,35 +403,6 @@ struct hb_sanitize_context_t : return likely (this->check_point ((const char *) obj + obj->min_size)); } - bool may_edit (const void *base, unsigned int len) - { - if (this->edit_count >= HB_SANITIZE_MAX_EDITS) - return false; - - const char *p = (const char *) base; - this->edit_count++; - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "may_edit(%u) [%p..%p] (%u bytes) in [%p..%p] -> %s", - this->edit_count, - p, p + len, len, - this->start, this->end, - this->writable ? "GRANTED" : "DENIED"); - - return this->writable; - } - - template - bool try_set (const Type *obj, const ValueType &v) - { - if (this->may_edit (obj, hb_static_size (Type))) - { - * const_cast (obj) = v; - return true; - } - return false; - } - template hb_blob_t *sanitize_blob (hb_blob_t *blob) { @@ -432,7 +410,6 @@ struct hb_sanitize_context_t : init (blob); - retry: DEBUG_MSG_FUNC (SANITIZE, start, "start"); start_processing (); @@ -446,36 +423,6 @@ struct hb_sanitize_context_t : Type *t = reinterpret_cast (const_cast (start)); sane = t->sanitize (this); - if (sane) - { - if (edit_count) - { - DEBUG_MSG_FUNC (SANITIZE, start, "passed first round with %u edits; going for second round", edit_count); - - /* sanitize again to ensure no toe-stepping */ - edit_count = 0; - sane = t->sanitize (this); - if (edit_count) { - DEBUG_MSG_FUNC (SANITIZE, start, "requested %u edits in second round; FAILING", edit_count); - sane = false; - } - } - } - else - { - if (edit_count && !writable) { - start = hb_blob_get_data_writable (blob, nullptr); - end = start + blob->length; - - if (start) - { - writable = true; - /* ok, we made it writable by relocating. try again */ - DEBUG_MSG_FUNC (SANITIZE, start, "retry"); - goto retry; - } - } - } end_processing (); @@ -506,7 +453,6 @@ struct hb_sanitize_context_t : private: int recursion_depth; bool writable; - unsigned int edit_count; hb_blob_t *blob; unsigned int num_glyphs; bool num_glyphs_set; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-script-list.h b/src/java.desktop/share/native/libharfbuzz/hb-script-list.h index f811dbc3408..16347b9d84e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-script-list.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-script-list.h @@ -223,6 +223,10 @@ HB_END_DECLS * @HB_SCRIPT_SUNUWAR: `Sunu`, Since: 10.0.0 * @HB_SCRIPT_TODHRI: `Todr`, Since: 10.0.0 * @HB_SCRIPT_TULU_TIGALARI: `Tutg`, Since: 10.0.0 + * @HB_SCRIPT_BERIA_ERFE: `Berf`, Since: 11.5.0 + * @HB_SCRIPT_SIDETIC: `Sidt`, Since: 11.5.0 + * @HB_SCRIPT_TAI_YO: `Tayo`, Since: 11.5.0 + * @HB_SCRIPT_TOLONG_SIKI: `Tols`, Since: 11.5.0 * @HB_SCRIPT_INVALID: No script set * * Data type for scripts. Each #hb_script_t's value is an #hb_tag_t corresponding @@ -461,6 +465,14 @@ typedef enum HB_SCRIPT_TODHRI = HB_TAG ('T','o','d','r'), /*16.0*/ HB_SCRIPT_TULU_TIGALARI = HB_TAG ('T','u','t','g'), /*16.0*/ + /* + * Since 11.5.0 + */ + HB_SCRIPT_BERIA_ERFE = HB_TAG ('B','e','r','f'), /*17.0*/ + HB_SCRIPT_SIDETIC = HB_TAG ('S','i','d','t'), /*17.0*/ + HB_SCRIPT_TAI_YO = HB_TAG ('T','a','y','o'), /*17.0*/ + HB_SCRIPT_TOLONG_SIKI = HB_TAG ('T','o','l','s'), /*17.0*/ + /* No script set. */ HB_SCRIPT_INVALID = HB_TAG_NONE, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh b/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh index 704d0ffd12b..9fe99c85ea7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh @@ -34,7 +34,7 @@ #include "hb.hh" #include "hb-blob.hh" #include "hb-map.hh" -#include "hb-pool.hh" +#include "hb-free-pool.hh" #include "hb-subset-serialize.h" @@ -724,7 +724,7 @@ struct hb_serialize_context_t hb_requires (hb_is_iterator (Iterator)), typename ...Ts> void copy_all (Iterator it, Ts&&... ds) - { for (decltype (*it) _ : it) copy (_, std::forward (ds)...); } + { for (decltype (*it) _ : it) copy (_, ds...); } template hb_serialize_context_t& operator << (const Type &obj) & { embed (obj); return *this; } @@ -794,7 +794,8 @@ struct hb_serialize_context_t template void assign_offset (const object_t* parent, const object_t::link_t &link, unsigned offset) { - auto &off = * ((BEInt *) (parent->head + link.position)); + // XXX We should stop assuming big-endian! + auto &off = * ((HBInt *) (parent->head + link.position)); assert (0 == off); check_assign (off, offset, HB_SERIALIZE_ERROR_OFFSET_OVERFLOW); } @@ -814,7 +815,7 @@ struct hb_serialize_context_t } /* Object memory pool. */ - hb_pool_t object_pool; + hb_free_pool_t object_pool; /* Stack of currently under construction objects. */ object_t *current; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh b/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh index f0416d5fa1d..f8cb0998260 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh @@ -55,11 +55,11 @@ * - For each glyph, if it doesn't match the subtable digest, * skip it. * - * The main filter we use is a combination of four bits-pattern + * The filter we use is a combination of three bits-pattern * filters. A bits-pattern filter checks a number of bits (5 or 6) - * of the input number (glyph-id in this case) and checks whether + * of the input number (glyph-id in most cases) and checks whether * its pattern is amongst the patterns of any of the accepted values. - * The accepted patterns are represented as a "long" integer. The + * The accepted patterns are represented as a "long" integer. Each * check is done using four bitwise operations only. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-shape.cc index d14f577be00..a36fa14fb28 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shape.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-shape.cc @@ -149,14 +149,11 @@ hb_shape_full (hb_font_t *font, hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features); - if (buffer->max_ops <= 0) - buffer->shaping_failed = true; - hb_shape_plan_destroy (shape_plan); if (text_buffer) { - if (res && buffer->successful && !buffer->shaping_failed + if (res && buffer->successful && text_buffer->successful && !buffer->verify (text_buffer, font, @@ -199,6 +196,7 @@ hb_shape (hb_font_t *font, #ifdef HB_EXPERIMENTAL_API +#ifndef HB_NO_VAR static float buffer_advance (hb_buffer_t *buffer) @@ -440,7 +438,7 @@ hb_shape_justify (hb_font_t *font, return true; } - +#endif #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh index f079caf4d35..cb386f26827 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh @@ -57,6 +57,14 @@ HB_SHAPER_IMPLEMENT (directwrite) HB_SHAPER_IMPLEMENT (coretext) #endif +#ifdef HAVE_HARFRUST +HB_SHAPER_IMPLEMENT (harfrust) +#endif + +#ifdef HAVE_KBTS +HB_SHAPER_IMPLEMENT (kbts) +#endif + #ifndef HB_NO_FALLBACK_SHAPE HB_SHAPER_IMPLEMENT (fallback) /* <--- This should be last. */ #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-static.cc b/src/java.desktop/share/native/libharfbuzz/hb-static.cc index 4e70e413651..06cc80b5f38 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-static.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-static.cc @@ -113,27 +113,4 @@ hb_face_t::load_upem () const return ret; } - -#ifndef HB_NO_VAR -bool -_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical, - int *lsb) -{ - return font->face->table.glyf->get_leading_bearing_with_var_unscaled (font, glyph, is_vertical, lsb); -} - -unsigned -_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical) -{ - return font->face->table.glyf->get_advance_with_var_unscaled (font, glyph, is_vertical); -} -#endif - -bool -_glyf_get_leading_bearing_without_var_unscaled (hb_face_t *face, hb_codepoint_t gid, bool is_vertical, int *lsb) -{ - return face->table.glyf->get_leading_bearing_without_var_unscaled (gid, is_vertical, lsb); -} - - #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh b/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh index 9d00cae6c32..51413b6da44 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh @@ -45,25 +45,25 @@ static const union HB_STRING_ARRAY_TYPE_NAME { * but C++ does not allow that. * https://stackoverflow.com/q/28433862 */ -#define _S(s) char HB_PASTE (str, __LINE__)[sizeof (s)]; +#define HB_STR(s) char HB_PASTE (str, __LINE__)[sizeof (s)]; #include HB_STRING_ARRAY_LIST -#undef _S +#undef HB_STR } st; char str[HB_VAR_ARRAY]; } HB_STRING_ARRAY_POOL_NAME = { { -#define _S(s) s, +#define HB_STR(s) s, #include HB_STRING_ARRAY_LIST -#undef _S +#undef HB_STR } }; static const unsigned int HB_STRING_ARRAY_OFFS_NAME[] = { -#define _S(s) offsetof (union HB_STRING_ARRAY_TYPE_NAME, st.HB_PASTE(str, __LINE__)), +#define HB_STR(s) offsetof (union HB_STRING_ARRAY_TYPE_NAME, st.HB_PASTE(str, __LINE__)), #include HB_STRING_ARRAY_LIST -#undef _S +#undef HB_STR sizeof (HB_STRING_ARRAY_TYPE_NAME) }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh index e7bddaef723..843ad69f2f9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh @@ -861,8 +861,7 @@ struct subr_subsetter_t { // Hack to point vector to static string. auto &b = buffArray.arrayZ[last]; - b.length = 1; - b.arrayZ = const_cast(endchar_str); + b.set_storage (const_cast(endchar_str), 1); } last++; // Skip over gid @@ -877,8 +876,7 @@ struct subr_subsetter_t { // Hack to point vector to static string. auto &b = buffArray.arrayZ[last]; - b.length = 1; - b.arrayZ = const_cast(endchar_str); + b.set_storage (const_cast(endchar_str), 1); } return true; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-iup.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-iup.hh index 01987bd258d..8ee02c07dbe 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-iup.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-iup.hh @@ -26,12 +26,27 @@ #define HB_SUBSET_INSTANCER_IUP_HH #include "hb-subset-plan.hh" + +struct iup_scratch_t +{ + hb_vector_t end_points; + hb_vector_t interp_x_deltas; + hb_vector_t interp_y_deltas; + hb_vector_t costs; + hb_vector_t chain; + hb_vector_t rot_indices; + hb_vector_t rot_x_deltas; + hb_vector_t rot_y_deltas; + contour_point_vector_t rot_points; +}; + /* given contour points and deltas, optimize a set of referenced points within error * tolerance. Returns optimized referenced point indices */ HB_INTERNAL bool iup_delta_optimize (const contour_point_vector_t& contour_points, const hb_vector_t& x_deltas, const hb_vector_t& y_deltas, hb_vector_t& opt_indices, /* OUT */ + iup_scratch_t &scratch, double tolerance = 0.0); #endif /* HB_SUBSET_INSTANCER_IUP_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.cc index c67fee421c2..15227bdab48 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.cc @@ -62,9 +62,10 @@ static inline double supportScalar (double coord, const Triple &tent) return (end - coord) / (end - peak); } -static inline rebase_tent_result_t -_solve (Triple tent, Triple axisLimit, bool negative = false) +static inline void +_solve (Triple tent, Triple axisLimit, rebase_tent_result_t &out, bool negative = false) { + out.reset(); double axisMin = axisLimit.minimum; double axisDef = axisLimit.middle; double axisMax = axisLimit.maximum; @@ -75,14 +76,12 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) // Mirror the problem such that axisDef <= peak if (axisDef > peak) { - rebase_tent_result_t vec = _solve (_reverse_negate (tent), - _reverse_negate (axisLimit), - !negative); + _solve (_reverse_negate (tent), _reverse_negate (axisLimit), out, !negative); - for (auto &p : vec) + for (auto &p : out) p = hb_pair (p.first, _reverse_negate (p.second)); - return vec; + return; } // axisDef <= peak @@ -98,7 +97,7 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) * axisMin axisDef axisMax lower upper */ if (axisMax <= lower && axisMax < peak) - return rebase_tent_result_t{}; // No overlap + return; // No overlap /* case 2: Only the peak and outermost bound fall outside the new limit; * we keep the deltaset, update peak and outermost bound and scale deltas @@ -133,18 +132,18 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) double mult = supportScalar (axisMax, tent); tent = Triple{lower, axisMax, axisMax}; - rebase_tent_result_t vec = _solve (tent, axisLimit); + _solve (tent, axisLimit, out); - for (auto &p : vec) + for (auto &p : out) p = hb_pair (p.first * mult, p.second); - return vec; + return; } // lower <= axisDef <= peak <= axisMax double gain = supportScalar (axisDef, tent); - rebase_tent_result_t out {hb_pair (gain, Triple{})}; + out.push(hb_pair (gain, Triple{})); // First, the positive side @@ -362,8 +361,6 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) out.push (hb_pair (scalar1 - gain, loc1)); out.push (hb_pair (scalar2 - gain, loc2)); } - - return out; } static inline TripleDistances _reverse_triple_distances (const TripleDistances &v) @@ -405,18 +402,21 @@ double renormalizeValue (double v, const Triple &triple, return (-v_distance) /total_distance; } -rebase_tent_result_t -rebase_tent (Triple tent, Triple axisLimit, TripleDistances axis_triple_distances) +void +rebase_tent (Triple tent, Triple axisLimit, TripleDistances axis_triple_distances, + rebase_tent_result_t &out, + rebase_tent_result_t &scratch) { assert (-1.0 <= axisLimit.minimum && axisLimit.minimum <= axisLimit.middle && axisLimit.middle <= axisLimit.maximum && axisLimit.maximum <= +1.0); assert (-2.0 <= tent.minimum && tent.minimum <= tent.middle && tent.middle <= tent.maximum && tent.maximum <= +2.0); assert (tent.middle != 0.0); - rebase_tent_result_t sols = _solve (tent, axisLimit); + rebase_tent_result_t &sols = scratch; + _solve (tent, axisLimit, sols); auto n = [&axisLimit, &axis_triple_distances] (double v) { return renormalizeValue (v, axisLimit, axis_triple_distances); }; - rebase_tent_result_t out; + out.reset(); for (auto &p : sols) { if (!p.first) continue; @@ -429,6 +429,4 @@ rebase_tent (Triple tent, Triple axisLimit, TripleDistances axis_triple_distance out.push (hb_pair (p.first, Triple{n (t.minimum), n (t.middle), n (t.maximum)})); } - - return out; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.hh index 9764dcc1725..574d58d53d8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.hh @@ -42,10 +42,9 @@ struct TripleDistances double positive; }; -struct Triple { - - Triple () : - minimum (0.0), middle (0.0), maximum (0.0) {} +struct Triple +{ + Triple () = default; Triple (double minimum_, double middle_, double maximum_) : minimum (minimum_), middle (middle_), maximum (maximum_) {} @@ -81,10 +80,9 @@ struct Triple { return current; } - - double minimum; - double middle; - double maximum; + double minimum = 0; + double middle = 0; + double maximum = 0; }; using rebase_tent_result_item_t = hb_pair_t; @@ -107,8 +105,10 @@ HB_INTERNAL double renormalizeValue (double v, const Triple &triple, * If tent value is Triple{}, that is a special deltaset that should * be always-enabled (called "gain"). */ -HB_INTERNAL rebase_tent_result_t rebase_tent (Triple tent, - Triple axisLimit, - TripleDistances axis_triple_distances); +HB_INTERNAL void rebase_tent (Triple tent, + Triple axisLimit, + TripleDistances axis_triple_distances, + rebase_tent_result_t &out, + rebase_tent_result_t &scratch); #endif /* HB_SUBSET_INSTANCER_SOLVER_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan-member-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan-member-list.hh index ade8278c40f..398fe81eca9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan-member-list.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan-member-list.hh @@ -81,6 +81,10 @@ HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(>), gpo HB_SUBSET_PLAN_MEMBER (hb_map_t, gsub_features) HB_SUBSET_PLAN_MEMBER (hb_map_t, gpos_features) +//active features(with duplicates) old index -> new index mapping +HB_SUBSET_PLAN_MEMBER (hb_map_t, gsub_features_w_duplicates) +HB_SUBSET_PLAN_MEMBER (hb_map_t, gpos_features_w_duplicates) + //active feature variation records/condition index with variations HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(>), gsub_feature_record_cond_idx_map) HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(>), gpos_feature_record_cond_idx_map) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc index 3ba726d925e..8c3fd97899b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc @@ -418,7 +418,8 @@ _nameid_closure (hb_subset_plan_t* plan, hb_set_t* drop_tables) { #ifndef HB_NO_STYLE - plan->source->table.STAT->collect_name_ids (&plan->user_axes_location, &plan->name_ids); + if (!drop_tables->has (HB_OT_TAG_STAT)) + plan->source->table.STAT->collect_name_ids (&plan->user_axes_location, &plan->name_ids); #endif #ifndef HB_NO_VAR if (!plan->all_axes_pinned) @@ -676,7 +677,8 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, return; #ifndef HB_NO_VAR - normalize_axes_location (face, this); + if (!check_success (normalize_axes_location (face, this))) + return; #endif _populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, this); @@ -697,6 +699,15 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, return; } +#ifdef HB_EXPERIMENTAL_API + if ((input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS) && + (input->flags & HB_SUBSET_FLAGS_RETAIN_NUM_GLYPHS)) { + // We've been requested to maintain the num glyphs count from the + // input face. + _num_output_glyphs = source->get_num_glyphs (); + } +#endif + _create_glyph_map_gsub ( &_glyphset_gsub, glyph_map, @@ -710,10 +721,10 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, glyph_map->get(unicode_to_new_gid_list.arrayZ[i].second); } - bounds_width_vec.resize (_num_output_glyphs, false); + bounds_width_vec.resize_dirty (_num_output_glyphs); for (auto &v : bounds_width_vec) v = 0xFFFFFFFF; - bounds_height_vec.resize (_num_output_glyphs, false); + bounds_height_vec.resize_dirty (_num_output_glyphs); for (auto &v : bounds_height_vec) v = 0xFFFFFFFF; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh index 8a6574365ed..0362ed7292e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh @@ -300,6 +300,7 @@ struct hb_subset_plan_t // compile times more reasonable: // - hb-subset-plan.cc // - hb-subset-plan-layout.cc +// - hb-subset-plan-var.cc // // The functions below are those needed to connect the split files // above together. @@ -332,7 +333,7 @@ generate_varstore_inner_maps (const hb_set_t& varidx_set, unsigned subtable_count, hb_vector_t &inner_maps /* OUT */); -HB_INTERNAL void +HB_INTERNAL bool normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan); HB_INTERNAL void diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset.cc index 079c94eddfe..51134ed09ce 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.cc @@ -25,65 +25,19 @@ */ #include "hb.hh" + #include "hb-open-type.hh" +#include "hb-open-file.hh" #include "hb-subset.hh" - -#include "hb-open-file.hh" -#include "hb-ot-cmap-table.hh" -#include "hb-ot-glyf-table.hh" -#include "hb-ot-hdmx-table.hh" -#include "hb-ot-head-table.hh" -#include "hb-ot-hhea-table.hh" -#include "hb-ot-hmtx-table.hh" -#include "hb-ot-maxp-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 "hb-ot-os2-table.hh" -#include "hb-ot-post-table.hh" -#include "hb-ot-post-table-v2subset.hh" -#include "hb-ot-cff1-table.hh" -#include "hb-ot-cff2-table.hh" -#include "hb-ot-vorg-table.hh" -#include "hb-ot-name-table.hh" -#include "hb-ot-layout-base-table.hh" -#include "hb-ot-layout-gsub-table.hh" -#include "hb-ot-layout-gpos-table.hh" -#include "hb-ot-var-avar-table.hh" -#include "hb-ot-var-cvar-table.hh" -#include "hb-ot-var-fvar-table.hh" -#include "hb-ot-var-gvar-table.hh" -#include "hb-ot-var-hvar-table.hh" -#include "hb-ot-var-mvar-table.hh" -#include "hb-ot-math-table.hh" -#include "hb-ot-stat-table.hh" -#include "hb-repacker.hh" +#include "hb-subset-table.hh" #include "hb-subset-accelerator.hh" -using OT::Layout::GSUB; -using OT::Layout::GPOS; - - -#ifndef HB_NO_SUBSET_CFF -template<> -struct hb_subset_plan_t::source_table_loader -{ - auto operator () (hb_subset_plan_t *plan) - HB_AUTO_RETURN (plan->accelerator ? plan->accelerator->cff1_accel : - plan->inprogress_accelerator ? plan->inprogress_accelerator->cff1_accel : - plan->cff1_accel) -}; -template<> -struct hb_subset_plan_t::source_table_loader -{ - auto operator () (hb_subset_plan_t *plan) - HB_AUTO_RETURN (plan->accelerator ? plan->accelerator->cff2_accel : - plan->inprogress_accelerator ? plan->inprogress_accelerator->cff2_accel : - plan->cff2_accel) -}; -#endif +#include "hb-ot-cmap-table.hh" +#include "hb-ot-var-cvar-table.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-stat-table.hh" +#include "hb-ot-post-table-v2subset.hh" /** @@ -116,56 +70,56 @@ hb_user_data_key_t _hb_subset_accelerator_user_data_key = {}; * if we are unable to list the tables in a face. */ static hb_tag_t known_tables[] { - HB_TAG ('a', 'v', 'a', 'r'), - HB_OT_TAG_BASE, - HB_OT_TAG_CBDT, - HB_OT_TAG_CBLC, - HB_OT_TAG_CFF1, - HB_OT_TAG_CFF2, - HB_OT_TAG_cmap, - HB_OT_TAG_COLR, - HB_OT_TAG_CPAL, - HB_TAG ('c', 'v', 'a', 'r'), - HB_TAG ('c', 'v', 't', ' '), - HB_TAG ('D', 'S', 'I', 'G'), - HB_TAG ('E', 'B', 'D', 'T'), - HB_TAG ('E', 'B', 'L', 'C'), - HB_TAG ('E', 'B', 'S', 'C'), - HB_TAG ('f', 'p', 'g', 'm'), - HB_TAG ('f', 'v', 'a', 'r'), - HB_TAG ('g', 'a', 's', 'p'), - HB_OT_TAG_GDEF, - HB_OT_TAG_glyf, - HB_OT_TAG_GPOS, - HB_OT_TAG_GSUB, - HB_OT_TAG_gvar, - HB_OT_TAG_hdmx, - HB_OT_TAG_head, - HB_OT_TAG_hhea, - HB_OT_TAG_hmtx, - HB_OT_TAG_HVAR, - HB_OT_TAG_JSTF, - HB_TAG ('k', 'e', 'r', 'n'), - HB_OT_TAG_loca, - HB_TAG ('L', 'T', 'S', 'H'), - HB_OT_TAG_MATH, - HB_OT_TAG_maxp, - HB_TAG ('M', 'E', 'R', 'G'), - HB_TAG ('m', 'e', 't', 'a'), - HB_TAG ('M', 'V', 'A', 'R'), - HB_TAG ('P', 'C', 'L', 'T'), - HB_OT_TAG_post, - HB_TAG ('p', 'r', 'e', 'p'), - HB_OT_TAG_sbix, - HB_TAG ('S', 'T', 'A', 'T'), - HB_TAG ('S', 'V', 'G', ' '), - HB_TAG ('V', 'D', 'M', 'X'), - HB_OT_TAG_vhea, - HB_OT_TAG_vmtx, - HB_OT_TAG_VORG, - HB_OT_TAG_VVAR, - HB_OT_TAG_name, - HB_OT_TAG_OS2 + HB_TAG('a','v','a','r'), + HB_TAG('B','A','S','E'), + HB_TAG('C','B','D','T'), + HB_TAG('C','B','L','C'), + HB_TAG('C','F','F',' '), + HB_TAG('C','F','F','2'), + HB_TAG('c','m','a','p'), + HB_TAG('C','O','L','R'), + HB_TAG('C','P','A','L'), + HB_TAG('c','v','a','r'), + HB_TAG('c','v','t',' '), + HB_TAG('D','S','I','G'), + HB_TAG('E','B','D','T'), + HB_TAG('E','B','L','C'), + HB_TAG('E','B','S','C'), + HB_TAG('f','p','g','m'), + HB_TAG('f','v','a','r'), + HB_TAG('g','a','s','p'), + HB_TAG('G','D','E','F'), + HB_TAG('g','l','y','f'), + HB_TAG('G','P','O','S'), + HB_TAG('G','S','U','B'), + HB_TAG('g','v','a','r'), + HB_TAG('h','d','m','x'), + HB_TAG('h','e','a','d'), + HB_TAG('h','h','e','a'), + HB_TAG('h','m','t','x'), + HB_TAG('H','V','A','R'), + HB_TAG('J','S','T','F'), + HB_TAG('k','e','r','n'), + HB_TAG('l','o','c','a'), + HB_TAG('L','T','S','H'), + HB_TAG('M','A','T','H'), + HB_TAG('m','a','x','p'), + HB_TAG('M','E','R','G'), + HB_TAG('m','e','t','a'), + HB_TAG('M','V','A','R'), + HB_TAG('P','C','L','T'), + HB_TAG('p','o','s','t'), + HB_TAG('p','r','e','p'), + HB_TAG('s','b','i','x'), + HB_TAG('S','T','A','T'), + HB_TAG('S','V','G',' '), + HB_TAG('V','D','M','X'), + HB_TAG('v','h','e','a'), + HB_TAG('v','m','t','x'), + HB_TAG('V','O','R','G'), + HB_TAG('V','V','A','R'), + HB_TAG('n','a','m','e'), + HB_TAG('O','S','/','2') }; static bool _table_is_empty (const hb_face_t *face, hb_tag_t tag) @@ -213,169 +167,6 @@ _get_table_tags (const hb_subset_plan_t* plan, } -static unsigned -_plan_estimate_subset_table_size (hb_subset_plan_t *plan, - unsigned table_len, - hb_tag_t table_tag) -{ - unsigned src_glyphs = plan->source->get_num_glyphs (); - unsigned dst_glyphs = plan->glyphset ()->get_population (); - - unsigned bulk = 8192; - /* Tables that we want to allocate same space as the source table. For GSUB/GPOS it's - * because those are expensive to subset, so giving them more room is fine. */ - bool same_size = table_tag == HB_OT_TAG_GSUB || - table_tag == HB_OT_TAG_GPOS || - table_tag == HB_OT_TAG_name; - - if (plan->flags & HB_SUBSET_FLAGS_RETAIN_GIDS) - { - if (table_tag == HB_OT_TAG_CFF1) - { - /* Add some extra room for the CFF charset. */ - bulk += src_glyphs * 16; - } - else if (table_tag == HB_OT_TAG_CFF2) - { - /* Just extra CharString offsets. */ - bulk += src_glyphs * 4; - } - } - - if (unlikely (!src_glyphs) || same_size) - return bulk + table_len; - - return bulk + (unsigned) (table_len * sqrt ((double) dst_glyphs / src_glyphs)); -} - -/* - * Repack the serialization buffer if any offset overflows exist. - */ -static hb_blob_t* -_repack (hb_tag_t tag, const hb_serialize_context_t& c) -{ - if (!c.offset_overflow ()) - return c.copy_blob (); - - hb_blob_t* result = hb_resolve_overflows (c.object_graph (), tag); - - if (unlikely (!result)) - { - DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c offset overflow resolution failed.", - HB_UNTAG (tag)); - return nullptr; - } - - return result; -} - -template -static -bool -_try_subset (const TableType *table, - hb_vector_t* buf, - hb_subset_context_t* c /* OUT */) -{ - c->serializer->start_serialize (); - if (c->serializer->in_error ()) return false; - - bool needed = table->subset (c); - if (!c->serializer->ran_out_of_room ()) - { - c->serializer->end_serialize (); - return needed; - } - - unsigned buf_size = buf->allocated; - buf_size = buf_size * 2 + 16; - - - - - DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.", - HB_UNTAG (c->table_tag), buf_size); - - if (unlikely (buf_size > c->source_blob->length * 256 || - !buf->alloc_exact (buf_size))) - { - DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.", - HB_UNTAG (c->table_tag), buf_size); - return needed; - } - - c->serializer->reset (buf->arrayZ, buf->allocated); - return _try_subset (table, buf, c); -} - -template -static auto _do_destroy (T &t, hb_priority<1>) HB_RETURN (void, t.destroy ()) - -template -static void _do_destroy (T &t, hb_priority<0>) {} - -template -static bool -_subset (hb_subset_plan_t *plan, hb_vector_t &buf) -{ - auto &&source_blob = plan->source_table (); - auto *table = source_blob.get (); - - hb_tag_t tag = TableType::tableTag; - hb_blob_t *blob = source_blob.get_blob(); - if (unlikely (!blob || !blob->data)) - { - DEBUG_MSG (SUBSET, nullptr, - "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag)); - _do_destroy (source_blob, hb_prioritize); - return false; - } - - unsigned buf_size = _plan_estimate_subset_table_size (plan, blob->length, TableType::tableTag); - DEBUG_MSG (SUBSET, nullptr, - "OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size); - if (unlikely (!buf.alloc (buf_size))) - { - DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to allocate %u bytes.", HB_UNTAG (tag), buf_size); - _do_destroy (source_blob, hb_prioritize); - return false; - } - - bool needed = false; - hb_serialize_context_t serializer (buf.arrayZ, buf.allocated); - { - hb_subset_context_t c (blob, plan, &serializer, tag); - needed = _try_subset (table, &buf, &c); - } - _do_destroy (source_blob, hb_prioritize); - - if (serializer.in_error () && !serializer.only_offset_overflow ()) - { - DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset FAILED!", HB_UNTAG (tag)); - return false; - } - - if (!needed) - { - DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag)); - return true; - } - - bool result = false; - hb_blob_t *dest_blob = _repack (tag, serializer); - if (dest_blob) - { - DEBUG_MSG (SUBSET, nullptr, - "OT::%c%c%c%c final subset table size: %u bytes.", - HB_UNTAG (tag), dest_blob->length); - result = plan->add_table (tag, dest_blob); - hb_blob_destroy (dest_blob); - } - - DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset %s", - HB_UNTAG (tag), result ? "success" : "FAILED!"); - return result; -} - static bool _is_table_present (hb_face_t *source, hb_tag_t tag) { @@ -407,34 +198,34 @@ _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) switch (tag) { - case HB_TAG ('c','v','a','r'): /* hint table, fallthrough */ + case HB_TAG('c','v','a','r'): /* hint table, fallthrough */ return plan->all_axes_pinned || (plan->flags & HB_SUBSET_FLAGS_NO_HINTING); - case HB_TAG ('c','v','t',' '): /* hint table, fallthrough */ - case HB_TAG ('f','p','g','m'): /* hint table, fallthrough */ - case HB_TAG ('p','r','e','p'): /* hint table, fallthrough */ - case HB_TAG ('h','d','m','x'): /* hint table, fallthrough */ - case HB_TAG ('V','D','M','X'): /* hint table, fallthrough */ + case HB_TAG('c','v','t',' '): /* hint table, fallthrough */ + case HB_TAG('f','p','g','m'): /* hint table, fallthrough */ + case HB_TAG('p','r','e','p'): /* hint table, fallthrough */ + case HB_TAG('h','d','m','x'): /* hint table, fallthrough */ + case HB_TAG('V','D','M','X'): /* hint table, fallthrough */ return plan->flags & HB_SUBSET_FLAGS_NO_HINTING; #ifdef HB_NO_SUBSET_LAYOUT // Drop Layout Tables if requested. - case HB_OT_TAG_GDEF: - case HB_OT_TAG_GPOS: - case HB_OT_TAG_GSUB: - case HB_TAG ('m','o','r','x'): - case HB_TAG ('m','o','r','t'): - case HB_TAG ('k','e','r','x'): - case HB_TAG ('k','e','r','n'): + case HB_TAG('G','D','E','F'): + case HB_TAG('G','P','O','S'): + case HB_TAG('G','S','U','B'): + case HB_TAG('m','o','r','x'): + case HB_TAG('m','o','r','t'): + case HB_TAG('k','e','r','x'): + case HB_TAG('k','e','r','n'): return true; #endif - case HB_TAG ('a','v','a','r'): - case HB_TAG ('f','v','a','r'): - case HB_TAG ('g','v','a','r'): - case HB_OT_TAG_HVAR: - case HB_OT_TAG_VVAR: - case HB_TAG ('M','V','A','R'): + case HB_TAG('a','v','a','r'): + case HB_TAG('f','v','a','r'): + case HB_TAG('g','v','a','r'): + case HB_TAG('H','V','A','R'): + case HB_TAG('V','V','A','R'): + case HB_TAG('M','V','A','R'): return plan->all_axes_pinned; default: @@ -442,15 +233,6 @@ _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) } } -static bool -_passthrough (hb_subset_plan_t *plan, hb_tag_t tag) -{ - hb_blob_t *source_table = hb_face_reference_table (plan->source, tag); - bool result = plan->add_table (tag, source_table); - hb_blob_destroy (source_table); - return result; -} - static bool _dependencies_satisfied (hb_subset_plan_t *plan, hb_tag_t tag, const hb_set_t &subsetted_tags, @@ -458,13 +240,13 @@ _dependencies_satisfied (hb_subset_plan_t *plan, hb_tag_t tag, { switch (tag) { - case HB_OT_TAG_hmtx: - case HB_OT_TAG_vmtx: - case HB_OT_TAG_maxp: - case HB_OT_TAG_OS2: - return !plan->normalized_coords || !pending_subset_tags.has (HB_OT_TAG_glyf); - case HB_OT_TAG_GPOS: - return plan->all_axes_pinned || !pending_subset_tags.has (HB_OT_TAG_GDEF); + case HB_TAG('h','m','t','x'): + case HB_TAG('v','m','t','x'): + case HB_TAG('m','a','x','p'): + case HB_TAG('O','S','/','2'): + return !plan->normalized_coords || !pending_subset_tags.has (HB_TAG('g','l','y','f')); + case HB_TAG('G','P','O','S'): + return plan->all_axes_pinned || !pending_subset_tags.has (HB_TAG('G','D','E','F')); default: return true; } @@ -476,88 +258,48 @@ _subset_table (hb_subset_plan_t *plan, hb_tag_t tag) { if (plan->no_subset_tables.has (tag)) { - return _passthrough (plan, tag); + return _hb_subset_table_passthrough (plan, tag); } DEBUG_MSG (SUBSET, nullptr, "subset %c%c%c%c", HB_UNTAG (tag)); + + bool success; + if (_hb_subset_table_layout (plan, buf, tag, &success) || + _hb_subset_table_var (plan, buf, tag, &success) || + _hb_subset_table_cff (plan, buf, tag, &success) || + _hb_subset_table_color (plan, buf, tag, &success) || + _hb_subset_table_other (plan, buf, tag, &success)) + return success; + + switch (tag) { - case HB_OT_TAG_glyf: return _subset (plan, buf); - case HB_OT_TAG_hdmx: return _subset (plan, buf); - case HB_OT_TAG_name: return _subset (plan, buf); - case HB_OT_TAG_head: - if (_is_table_present (plan->source, HB_OT_TAG_glyf) && !_should_drop_table (plan, HB_OT_TAG_glyf)) + case HB_TAG('h','e','a','d'): + if (_is_table_present (plan->source, HB_TAG('g','l','y','f')) && !_should_drop_table (plan, HB_TAG('g','l','y','f'))) return true; /* skip head, handled by glyf */ - return _subset (plan, buf); - case HB_OT_TAG_hhea: return true; /* skip hhea, handled by hmtx */ - case HB_OT_TAG_hmtx: return _subset (plan, buf); - case HB_OT_TAG_vhea: return true; /* skip vhea, handled by vmtx */ - case HB_OT_TAG_vmtx: return _subset (plan, buf); - case HB_OT_TAG_maxp: return _subset (plan, buf); - case HB_OT_TAG_sbix: return _subset (plan, buf); - case HB_OT_TAG_loca: return true; /* skip loca, handled by glyf */ - case HB_OT_TAG_cmap: return _subset (plan, buf); - case HB_OT_TAG_OS2 : return _subset (plan, buf); - case HB_OT_TAG_post: return _subset (plan, buf); - case HB_OT_TAG_COLR: return _subset (plan, buf); - case HB_OT_TAG_CPAL: return _subset (plan, buf); - case HB_OT_TAG_CBLC: return _subset (plan, buf); - case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */ - case HB_OT_TAG_MATH: return _subset (plan, buf); - case HB_OT_TAG_BASE: return _subset (plan, buf); + return _hb_subset_table (plan, buf); -#ifndef HB_NO_SUBSET_CFF - case HB_OT_TAG_CFF1: return _subset (plan, buf); - case HB_OT_TAG_CFF2: return _subset (plan, buf); - case HB_OT_TAG_VORG: return _subset (plan, buf); -#endif - -#ifndef HB_NO_SUBSET_LAYOUT - case HB_OT_TAG_GDEF: return _subset (plan, buf); - case HB_OT_TAG_GSUB: return _subset (plan, buf); - case HB_OT_TAG_GPOS: return _subset (plan, buf); - case HB_OT_TAG_gvar: return _subset (plan, buf); - case HB_OT_TAG_HVAR: return _subset (plan, buf); - case HB_OT_TAG_VVAR: return _subset (plan, buf); -#endif + case HB_TAG('S','T','A','T'): + if (!plan->user_axes_location.is_empty ()) return _hb_subset_table (plan, buf); + else return _hb_subset_table_passthrough (plan, tag); + case HB_TAG('c','v','t',' '): #ifndef HB_NO_VAR - case HB_OT_TAG_fvar: - if (plan->user_axes_location.is_empty ()) return _passthrough (plan, tag); - return _subset (plan, buf); - case HB_OT_TAG_avar: - if (plan->user_axes_location.is_empty ()) return _passthrough (plan, tag); - return _subset (plan, buf); - case HB_OT_TAG_cvar: - if (plan->user_axes_location.is_empty ()) return _passthrough (plan, tag); - return _subset (plan, buf); - case HB_OT_TAG_MVAR: - if (plan->user_axes_location.is_empty ()) return _passthrough (plan, tag); - return _subset (plan, buf); -#endif - - case HB_OT_TAG_STAT: - if (!plan->user_axes_location.is_empty ()) return _subset (plan, buf); - else return _passthrough (plan, tag); - - case HB_TAG ('c', 'v', 't', ' '): -#ifndef HB_NO_VAR - if (_is_table_present (plan->source, HB_OT_TAG_cvar) && + if (_is_table_present (plan->source, HB_TAG('c','v','a','r')) && plan->normalized_coords && !plan->pinned_at_default) { auto &cvar = *plan->source->table.cvar; return OT::cvar::add_cvt_and_apply_deltas (plan, cvar.get_tuple_var_data (), &cvar); } #endif - return _passthrough (plan, tag); - - default: - if (plan->flags & HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED) - return _passthrough (plan, tag); - - // Drop table - return true; + return _hb_subset_table_passthrough (plan, tag); } + + if (plan->flags & HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED) + return _hb_subset_table_passthrough (plan, tag); + + // Drop table + return true; } static void _attach_accelerator_data (hb_subset_plan_t* plan, @@ -707,108 +449,4 @@ hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan) end: return success ? hb_face_reference (plan->dest) : nullptr; -} - - -#ifdef HB_EXPERIMENTAL_API - -#include "hb-ot-cff1-table.hh" - -template -static hb_blob_t* get_charstrings_data(accel_t& accel, hb_codepoint_t glyph_index) { - if (!accel.is_valid()) { - return hb_blob_get_empty (); - } - - hb_ubytes_t bytes = (*accel.charStrings)[glyph_index]; - if (!bytes) { - return hb_blob_get_empty (); - } - - hb_blob_t* cff_blob = accel.get_blob(); - uint32_t length; - const char* cff_data = hb_blob_get_data(cff_blob, &length) ; - - long int offset = (const char*) bytes.arrayZ - cff_data; - if (offset < 0 || offset > INT32_MAX) { - return hb_blob_get_empty (); - } - - return hb_blob_create_sub_blob(cff_blob, (uint32_t) offset, bytes.length); -} - -template -static hb_blob_t* get_charstrings_index(accel_t& accel) { - if (!accel.is_valid()) { - return hb_blob_get_empty (); - } - - const char* charstrings_start = (const char*) accel.charStrings; - unsigned charstrings_length = accel.charStrings->get_size(); - - hb_blob_t* cff_blob = accel.get_blob(); - uint32_t length; - const char* cff_data = hb_blob_get_data(cff_blob, &length) ; - - long int offset = charstrings_start - cff_data; - if (offset < 0 || offset > INT32_MAX) { - return hb_blob_get_empty (); - } - - return hb_blob_create_sub_blob(cff_blob, (uint32_t) offset, charstrings_length); -} - -/** - * hb_subset_cff_get_charstring_data: - * @face: A face object - * @glyph_index: Glyph index to get data for. - * - * Returns the raw outline data from the CFF/CFF2 table associated with the given glyph index. - * - * XSince: EXPERIMENTAL - **/ -HB_EXTERN hb_blob_t* -hb_subset_cff_get_charstring_data(hb_face_t* face, hb_codepoint_t glyph_index) { - return get_charstrings_data(*face->table.cff1, glyph_index); -} - -/** - * hb_subset_cff_get_charstrings_index: - * @face: A face object - * - * Returns the raw CFF CharStrings INDEX from the CFF table. - * - * XSince: EXPERIMENTAL - **/ -HB_EXTERN hb_blob_t* -hb_subset_cff_get_charstrings_index (hb_face_t* face) { - return get_charstrings_index (*face->table.cff1); -} - -/** - * hb_subset_cff2_get_charstring_data: - * @face: A face object - * @glyph_index: Glyph index to get data for. - * - * Returns the raw outline data from the CFF/CFF2 table associated with the given glyph index. - * - * XSince: EXPERIMENTAL - **/ -HB_EXTERN hb_blob_t* -hb_subset_cff2_get_charstring_data(hb_face_t* face, hb_codepoint_t glyph_index) { - return get_charstrings_data(*face->table.cff2, glyph_index); -} - -/** - * hb_subset_cff2_get_charstrings_index: - * @face: A face object - * - * Returns the raw CFF2 CharStrings INDEX from the CFF2 table. - * - * XSince: EXPERIMENTAL - **/ -HB_EXTERN hb_blob_t* -hb_subset_cff2_get_charstrings_index (hb_face_t* face) { - return get_charstrings_index (*face->table.cff2); -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.h b/src/java.desktop/share/native/libharfbuzz/hb-subset.h index 3eccd25738e..fd2908e8308 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.h @@ -80,6 +80,10 @@ typedef struct hb_subset_plan_t hb_subset_plan_t; * @HB_SUBSET_FLAGS_IFTB_REQUIREMENTS: If set enforce requirements on the output subset * to allow it to be used with incremental font transfer IFTB patches. Primarily, * this forces all outline data to use long (32 bit) offsets. Since: EXPERIMENTAL + * @HB_SUBSET_FLAGS_RETAIN_NUM_GLYPHS: If this flag is set along side + * HB_SUBSET_FLAGS_RETAIN_GIDS then the number of glyphs in the font won't + * be reduced as a result of subsetting. If necessary empty glyphs will be + * included at the end of the font to keep the number of glyphs unchanged. * * List of boolean properties that can be configured on the subset input. * @@ -101,6 +105,7 @@ typedef enum { /*< flags >*/ HB_SUBSET_FLAGS_NO_BIDI_CLOSURE = 0x00000800u, #ifdef HB_EXPERIMENTAL_API HB_SUBSET_FLAGS_IFTB_REQUIREMENTS = 0x00001000u, + HB_SUBSET_FLAGS_RETAIN_NUM_GLYPHS = 0x00002000u, #endif } hb_subset_flags_t; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset.hh index bc7c24c94de..009919764f5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.hh @@ -70,5 +70,4 @@ struct hb_subset_context_t : table_tag (table_tag_) {} }; - #endif /* HB_SUBSET_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh index 8731a0bcf8d..868428ab1e7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh @@ -2,9 +2,9 @@ /* * The following table is generated by running: * - * ./gen-ucd-table.py ucd.nounihan.grouped.xml + * ./gen-ucd-table.py ucd.nounihan.grouped.xml hb-script-list.h * - * on file with this description: Unicode 16.0.0 + * on file with this description: Unicode 17.0.0 */ #ifndef HB_UCD_TABLE_HH @@ -12,8 +12,9 @@ #include "hb.hh" -static const hb_script_t -_hb_ucd_sc_map[172] = +#include + +static const hb_script_t _hb_ucd_sc_map[176]= { HB_SCRIPT_COMMON, HB_SCRIPT_INHERITED, HB_SCRIPT_UNKNOWN, HB_SCRIPT_ARABIC, @@ -101,1040 +102,805 @@ _hb_ucd_sc_map[172] = HB_SCRIPT_GURUNG_KHEMA, HB_SCRIPT_KIRAT_RAI, HB_SCRIPT_OL_ONAL, HB_SCRIPT_SUNUWAR, HB_SCRIPT_TODHRI, HB_SCRIPT_TULU_TIGALARI, + HB_SCRIPT_BERIA_ERFE, HB_SCRIPT_SIDETIC, + HB_SCRIPT_TAI_YO, HB_SCRIPT_TOLONG_SIKI, }; -static const uint16_t -_hb_ucd_dm1_p0_map[825] = +static const uint16_t _hb_ucd_dm1_p0_map[825]= { - 0x003Bu, 0x004Bu, 0x0060u, 0x00B4u, 0x00B7u, 0x00C5u, 0x02B9u, 0x0300u, - 0x0301u, 0x0313u, 0x0385u, 0x0386u, 0x0388u, 0x0389u, 0x038Au, 0x038Cu, - 0x038Eu, 0x038Fu, 0x0390u, 0x03A9u, 0x03ACu, 0x03ADu, 0x03AEu, 0x03AFu, - 0x03B0u, 0x03B9u, 0x03CCu, 0x03CDu, 0x03CEu, 0x2002u, 0x2003u, 0x3008u, - 0x3009u, 0x349Eu, 0x34B9u, 0x34BBu, 0x34DFu, 0x3515u, 0x36EEu, 0x36FCu, - 0x3781u, 0x382Fu, 0x3862u, 0x387Cu, 0x38C7u, 0x38E3u, 0x391Cu, 0x393Au, - 0x3A2Eu, 0x3A6Cu, 0x3AE4u, 0x3B08u, 0x3B19u, 0x3B49u, 0x3B9Du, 0x3C18u, - 0x3C4Eu, 0x3D33u, 0x3D96u, 0x3EACu, 0x3EB8u, 0x3F1Bu, 0x3FFCu, 0x4008u, - 0x4018u, 0x4039u, 0x4046u, 0x4096u, 0x40E3u, 0x412Fu, 0x4202u, 0x4227u, - 0x42A0u, 0x4301u, 0x4334u, 0x4359u, 0x43D5u, 0x43D9u, 0x440Bu, 0x446Bu, - 0x452Bu, 0x455Du, 0x4561u, 0x456Bu, 0x45D7u, 0x45F9u, 0x4635u, 0x46BEu, - 0x46C7u, 0x4995u, 0x49E6u, 0x4A6Eu, 0x4A76u, 0x4AB2u, 0x4B33u, 0x4BCEu, - 0x4CCEu, 0x4CEDu, 0x4CF8u, 0x4D56u, 0x4E0Du, 0x4E26u, 0x4E32u, 0x4E38u, - 0x4E39u, 0x4E3Du, 0x4E41u, 0x4E82u, 0x4E86u, 0x4EAEu, 0x4EC0u, 0x4ECCu, - 0x4EE4u, 0x4F60u, 0x4F80u, 0x4F86u, 0x4F8Bu, 0x4FAEu, 0x4FBBu, 0x4FBFu, - 0x5002u, 0x502Bu, 0x507Au, 0x5099u, 0x50CFu, 0x50DAu, 0x50E7u, 0x5140u, - 0x5145u, 0x514Du, 0x5154u, 0x5164u, 0x5167u, 0x5168u, 0x5169u, 0x516Du, - 0x5177u, 0x5180u, 0x518Du, 0x5192u, 0x5195u, 0x5197u, 0x51A4u, 0x51ACu, - 0x51B5u, 0x51B7u, 0x51C9u, 0x51CCu, 0x51DCu, 0x51DEu, 0x51F5u, 0x5203u, - 0x5207u, 0x5217u, 0x5229u, 0x523Au, 0x523Bu, 0x5246u, 0x5272u, 0x5277u, - 0x5289u, 0x529Bu, 0x52A3u, 0x52B3u, 0x52C7u, 0x52C9u, 0x52D2u, 0x52DEu, - 0x52E4u, 0x52F5u, 0x52FAu, 0x5305u, 0x5306u, 0x5317u, 0x533Fu, 0x5349u, - 0x5351u, 0x535Au, 0x5373u, 0x5375u, 0x537Du, 0x537Fu, 0x53C3u, 0x53CAu, - 0x53DFu, 0x53E5u, 0x53EBu, 0x53F1u, 0x5406u, 0x540Fu, 0x541Du, 0x5438u, - 0x5442u, 0x5448u, 0x5468u, 0x549Eu, 0x54A2u, 0x54BDu, 0x54F6u, 0x5510u, - 0x5553u, 0x5555u, 0x5563u, 0x5584u, 0x5587u, 0x5599u, 0x559Du, 0x55ABu, - 0x55B3u, 0x55C0u, 0x55C2u, 0x55E2u, 0x5606u, 0x5651u, 0x5668u, 0x5674u, - 0x56F9u, 0x5716u, 0x5717u, 0x578Bu, 0x57CEu, 0x57F4u, 0x580Du, 0x5831u, - 0x5832u, 0x5840u, 0x585Au, 0x585Eu, 0x58A8u, 0x58ACu, 0x58B3u, 0x58D8u, - 0x58DFu, 0x58EEu, 0x58F2u, 0x58F7u, 0x5906u, 0x591Au, 0x5922u, 0x5944u, - 0x5948u, 0x5951u, 0x5954u, 0x5962u, 0x5973u, 0x59D8u, 0x59ECu, 0x5A1Bu, - 0x5A27u, 0x5A62u, 0x5A66u, 0x5AB5u, 0x5B08u, 0x5B28u, 0x5B3Eu, 0x5B85u, - 0x5BC3u, 0x5BD8u, 0x5BE7u, 0x5BEEu, 0x5BF3u, 0x5BFFu, 0x5C06u, 0x5C22u, - 0x5C3Fu, 0x5C60u, 0x5C62u, 0x5C64u, 0x5C65u, 0x5C6Eu, 0x5C8Du, 0x5CC0u, - 0x5D19u, 0x5D43u, 0x5D50u, 0x5D6Bu, 0x5D6Eu, 0x5D7Cu, 0x5DB2u, 0x5DBAu, - 0x5DE1u, 0x5DE2u, 0x5DFDu, 0x5E28u, 0x5E3Du, 0x5E69u, 0x5E74u, 0x5EA6u, - 0x5EB0u, 0x5EB3u, 0x5EB6u, 0x5EC9u, 0x5ECAu, 0x5ED2u, 0x5ED3u, 0x5ED9u, - 0x5EECu, 0x5EFEu, 0x5F04u, 0x5F22u, 0x5F53u, 0x5F62u, 0x5F69u, 0x5F6Bu, - 0x5F8Bu, 0x5F9Au, 0x5FA9u, 0x5FADu, 0x5FCDu, 0x5FD7u, 0x5FF5u, 0x5FF9u, - 0x6012u, 0x601Cu, 0x6075u, 0x6081u, 0x6094u, 0x60C7u, 0x60D8u, 0x60E1u, - 0x6108u, 0x6144u, 0x6148u, 0x614Cu, 0x614Eu, 0x6160u, 0x6168u, 0x617Au, - 0x618Eu, 0x6190u, 0x61A4u, 0x61AFu, 0x61B2u, 0x61DEu, 0x61F2u, 0x61F6u, - 0x6200u, 0x6210u, 0x621Bu, 0x622Eu, 0x6234u, 0x625Du, 0x62B1u, 0x62C9u, - 0x62CFu, 0x62D3u, 0x62D4u, 0x62FCu, 0x62FEu, 0x633Du, 0x6350u, 0x6368u, - 0x637Bu, 0x6383u, 0x63A0u, 0x63A9u, 0x63C4u, 0x63C5u, 0x63E4u, 0x641Cu, - 0x6422u, 0x6452u, 0x6469u, 0x6477u, 0x647Eu, 0x649Au, 0x649Du, 0x64C4u, - 0x654Fu, 0x6556u, 0x656Cu, 0x6578u, 0x6599u, 0x65C5u, 0x65E2u, 0x65E3u, - 0x6613u, 0x6649u, 0x6674u, 0x6688u, 0x6691u, 0x669Cu, 0x66B4u, 0x66C6u, - 0x66F4u, 0x66F8u, 0x6700u, 0x6717u, 0x671Bu, 0x6721u, 0x674Eu, 0x6753u, - 0x6756u, 0x675Eu, 0x677Bu, 0x6785u, 0x6797u, 0x67F3u, 0x67FAu, 0x6817u, - 0x681Fu, 0x6852u, 0x6881u, 0x6885u, 0x688Eu, 0x68A8u, 0x6914u, 0x6942u, - 0x69A3u, 0x69EAu, 0x6A02u, 0x6A13u, 0x6AA8u, 0x6AD3u, 0x6ADBu, 0x6B04u, - 0x6B21u, 0x6B54u, 0x6B72u, 0x6B77u, 0x6B79u, 0x6B9Fu, 0x6BAEu, 0x6BBAu, - 0x6BBBu, 0x6C4Eu, 0x6C67u, 0x6C88u, 0x6CBFu, 0x6CCCu, 0x6CCDu, 0x6CE5u, - 0x6D16u, 0x6D1Bu, 0x6D1Eu, 0x6D34u, 0x6D3Eu, 0x6D41u, 0x6D69u, 0x6D6Au, - 0x6D77u, 0x6D78u, 0x6D85u, 0x6DCBu, 0x6DDAu, 0x6DEAu, 0x6DF9u, 0x6E1Au, - 0x6E2Fu, 0x6E6Eu, 0x6E9Cu, 0x6EBAu, 0x6EC7u, 0x6ECBu, 0x6ED1u, 0x6EDBu, - 0x6F0Fu, 0x6F22u, 0x6F23u, 0x6F6Eu, 0x6FC6u, 0x6FEBu, 0x6FFEu, 0x701Bu, - 0x701Eu, 0x7039u, 0x704Au, 0x7070u, 0x7077u, 0x707Du, 0x7099u, 0x70ADu, - 0x70C8u, 0x70D9u, 0x7145u, 0x7149u, 0x716Eu, 0x719Cu, 0x71CEu, 0x71D0u, - 0x7210u, 0x721Bu, 0x7228u, 0x722Bu, 0x7235u, 0x7250u, 0x7262u, 0x7280u, - 0x7295u, 0x72AFu, 0x72C0u, 0x72FCu, 0x732Au, 0x7375u, 0x737Au, 0x7387u, - 0x738Bu, 0x73A5u, 0x73B2u, 0x73DEu, 0x7406u, 0x7409u, 0x7422u, 0x7447u, - 0x745Cu, 0x7469u, 0x7471u, 0x7485u, 0x7489u, 0x7498u, 0x74CAu, 0x7506u, - 0x7524u, 0x753Bu, 0x753Eu, 0x7559u, 0x7565u, 0x7570u, 0x75E2u, 0x7610u, - 0x761Du, 0x761Fu, 0x7642u, 0x7669u, 0x76CAu, 0x76DBu, 0x76E7u, 0x76F4u, - 0x7701u, 0x771Eu, 0x771Fu, 0x7740u, 0x774Au, 0x778Bu, 0x77A7u, 0x784Eu, - 0x786Bu, 0x788Cu, 0x7891u, 0x78CAu, 0x78CCu, 0x78FBu, 0x792Au, 0x793Cu, - 0x793Eu, 0x7948u, 0x7949u, 0x7950u, 0x7956u, 0x795Du, 0x795Eu, 0x7965u, - 0x797Fu, 0x798Du, 0x798Eu, 0x798Fu, 0x79AEu, 0x79CAu, 0x79EBu, 0x7A1Cu, - 0x7A40u, 0x7A4Au, 0x7A4Fu, 0x7A81u, 0x7AB1u, 0x7ACBu, 0x7AEEu, 0x7B20u, - 0x7BC0u, 0x7BC6u, 0x7BC9u, 0x7C3Eu, 0x7C60u, 0x7C7Bu, 0x7C92u, 0x7CBEu, - 0x7CD2u, 0x7CD6u, 0x7CE3u, 0x7CE7u, 0x7CE8u, 0x7D00u, 0x7D10u, 0x7D22u, - 0x7D2Fu, 0x7D5Bu, 0x7D63u, 0x7DA0u, 0x7DBEu, 0x7DC7u, 0x7DF4u, 0x7E02u, - 0x7E09u, 0x7E37u, 0x7E41u, 0x7E45u, 0x7F3Eu, 0x7F72u, 0x7F79u, 0x7F7Au, - 0x7F85u, 0x7F95u, 0x7F9Au, 0x7FBDu, 0x7FFAu, 0x8001u, 0x8005u, 0x8046u, - 0x8060u, 0x806Fu, 0x8070u, 0x807Eu, 0x808Bu, 0x80ADu, 0x80B2u, 0x8103u, - 0x813Eu, 0x81D8u, 0x81E8u, 0x81EDu, 0x8201u, 0x8204u, 0x8218u, 0x826Fu, - 0x8279u, 0x828Bu, 0x8291u, 0x829Du, 0x82B1u, 0x82B3u, 0x82BDu, 0x82E5u, - 0x82E6u, 0x831Du, 0x8323u, 0x8336u, 0x8352u, 0x8353u, 0x8363u, 0x83ADu, - 0x83BDu, 0x83C9u, 0x83CAu, 0x83CCu, 0x83DCu, 0x83E7u, 0x83EFu, 0x83F1u, - 0x843Du, 0x8449u, 0x8457u, 0x84EEu, 0x84F1u, 0x84F3u, 0x84FCu, 0x8516u, - 0x8564u, 0x85CDu, 0x85FAu, 0x8606u, 0x8612u, 0x862Du, 0x863Fu, 0x8650u, - 0x865Cu, 0x8667u, 0x8669u, 0x8688u, 0x86A9u, 0x86E2u, 0x870Eu, 0x8728u, - 0x876Bu, 0x8779u, 0x8786u, 0x87BAu, 0x87E1u, 0x8801u, 0x881Fu, 0x884Cu, - 0x8860u, 0x8863u, 0x88C2u, 0x88CFu, 0x88D7u, 0x88DEu, 0x88E1u, 0x88F8u, - 0x88FAu, 0x8910u, 0x8941u, 0x8964u, 0x8986u, 0x898Bu, 0x8996u, 0x8AA0u, - 0x8AAAu, 0x8ABFu, 0x8ACBu, 0x8AD2u, 0x8AD6u, 0x8AEDu, 0x8AF8u, 0x8AFEu, - 0x8B01u, 0x8B39u, 0x8B58u, 0x8B80u, 0x8B8Au, 0x8C48u, 0x8C55u, 0x8CABu, - 0x8CC1u, 0x8CC2u, 0x8CC8u, 0x8CD3u, 0x8D08u, 0x8D1Bu, 0x8D77u, 0x8DBCu, - 0x8DCBu, 0x8DEFu, 0x8DF0u, 0x8ECAu, 0x8ED4u, 0x8F26u, 0x8F2Au, 0x8F38u, - 0x8F3Bu, 0x8F62u, 0x8F9Eu, 0x8FB0u, 0x8FB6u, 0x9023u, 0x9038u, 0x9072u, - 0x907Cu, 0x908Fu, 0x9094u, 0x90CEu, 0x90DEu, 0x90F1u, 0x90FDu, 0x9111u, - 0x911Bu, 0x916Au, 0x9199u, 0x91B4u, 0x91CCu, 0x91CFu, 0x91D1u, 0x9234u, - 0x9238u, 0x9276u, 0x927Cu, 0x92D7u, 0x92D8u, 0x9304u, 0x934Au, 0x93F9u, - 0x9415u, 0x958Bu, 0x95ADu, 0x95B7u, 0x962Eu, 0x964Bu, 0x964Du, 0x9675u, - 0x9678u, 0x967Cu, 0x9686u, 0x96A3u, 0x96B7u, 0x96B8u, 0x96C3u, 0x96E2u, - 0x96E3u, 0x96F6u, 0x96F7u, 0x9723u, 0x9732u, 0x9748u, 0x9756u, 0x97DBu, - 0x97E0u, 0x97FFu, 0x980Bu, 0x9818u, 0x9829u, 0x983Bu, 0x985Eu, 0x98E2u, - 0x98EFu, 0x98FCu, 0x9928u, 0x9929u, 0x99A7u, 0x99C2u, 0x99F1u, 0x99FEu, - 0x9A6Au, 0x9B12u, 0x9B6Fu, 0x9C40u, 0x9C57u, 0x9CFDu, 0x9D67u, 0x9DB4u, - 0x9DFAu, 0x9E1Eu, 0x9E7Fu, 0x9E97u, 0x9E9Fu, 0x9EBBu, 0x9ECEu, 0x9EF9u, - 0x9EFEu, 0x9F05u, 0x9F0Fu, 0x9F16u, 0x9F3Bu, 0x9F43u, 0x9F8Du, 0x9F8Eu, - 0x9F9Cu, + 0x003B, 0x004B, 0x0060, 0x00B4, 0x00B7, 0x00C5, 0x02B9, 0x0300, + 0x0301, 0x0313, 0x0385, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, + 0x038E, 0x038F, 0x0390, 0x03A9, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + 0x03B0, 0x03B9, 0x03CC, 0x03CD, 0x03CE, 0x2002, 0x2003, 0x3008, + 0x3009, 0x349E, 0x34B9, 0x34BB, 0x34DF, 0x3515, 0x36EE, 0x36FC, + 0x3781, 0x382F, 0x3862, 0x387C, 0x38C7, 0x38E3, 0x391C, 0x393A, + 0x3A2E, 0x3A6C, 0x3AE4, 0x3B08, 0x3B19, 0x3B49, 0x3B9D, 0x3C18, + 0x3C4E, 0x3D33, 0x3D96, 0x3EAC, 0x3EB8, 0x3F1B, 0x3FFC, 0x4008, + 0x4018, 0x4039, 0x4046, 0x4096, 0x40E3, 0x412F, 0x4202, 0x4227, + 0x42A0, 0x4301, 0x4334, 0x4359, 0x43D5, 0x43D9, 0x440B, 0x446B, + 0x452B, 0x455D, 0x4561, 0x456B, 0x45D7, 0x45F9, 0x4635, 0x46BE, + 0x46C7, 0x4995, 0x49E6, 0x4A6E, 0x4A76, 0x4AB2, 0x4B33, 0x4BCE, + 0x4CCE, 0x4CED, 0x4CF8, 0x4D56, 0x4E0D, 0x4E26, 0x4E32, 0x4E38, + 0x4E39, 0x4E3D, 0x4E41, 0x4E82, 0x4E86, 0x4EAE, 0x4EC0, 0x4ECC, + 0x4EE4, 0x4F60, 0x4F80, 0x4F86, 0x4F8B, 0x4FAE, 0x4FBB, 0x4FBF, + 0x5002, 0x502B, 0x507A, 0x5099, 0x50CF, 0x50DA, 0x50E7, 0x5140, + 0x5145, 0x514D, 0x5154, 0x5164, 0x5167, 0x5168, 0x5169, 0x516D, + 0x5177, 0x5180, 0x518D, 0x5192, 0x5195, 0x5197, 0x51A4, 0x51AC, + 0x51B5, 0x51B7, 0x51C9, 0x51CC, 0x51DC, 0x51DE, 0x51F5, 0x5203, + 0x5207, 0x5217, 0x5229, 0x523A, 0x523B, 0x5246, 0x5272, 0x5277, + 0x5289, 0x529B, 0x52A3, 0x52B3, 0x52C7, 0x52C9, 0x52D2, 0x52DE, + 0x52E4, 0x52F5, 0x52FA, 0x5305, 0x5306, 0x5317, 0x533F, 0x5349, + 0x5351, 0x535A, 0x5373, 0x5375, 0x537D, 0x537F, 0x53C3, 0x53CA, + 0x53DF, 0x53E5, 0x53EB, 0x53F1, 0x5406, 0x540F, 0x541D, 0x5438, + 0x5442, 0x5448, 0x5468, 0x549E, 0x54A2, 0x54BD, 0x54F6, 0x5510, + 0x5553, 0x5555, 0x5563, 0x5584, 0x5587, 0x5599, 0x559D, 0x55AB, + 0x55B3, 0x55C0, 0x55C2, 0x55E2, 0x5606, 0x5651, 0x5668, 0x5674, + 0x56F9, 0x5716, 0x5717, 0x578B, 0x57CE, 0x57F4, 0x580D, 0x5831, + 0x5832, 0x5840, 0x585A, 0x585E, 0x58A8, 0x58AC, 0x58B3, 0x58D8, + 0x58DF, 0x58EE, 0x58F2, 0x58F7, 0x5906, 0x591A, 0x5922, 0x5944, + 0x5948, 0x5951, 0x5954, 0x5962, 0x5973, 0x59D8, 0x59EC, 0x5A1B, + 0x5A27, 0x5A62, 0x5A66, 0x5AB5, 0x5B08, 0x5B28, 0x5B3E, 0x5B85, + 0x5BC3, 0x5BD8, 0x5BE7, 0x5BEE, 0x5BF3, 0x5BFF, 0x5C06, 0x5C22, + 0x5C3F, 0x5C60, 0x5C62, 0x5C64, 0x5C65, 0x5C6E, 0x5C8D, 0x5CC0, + 0x5D19, 0x5D43, 0x5D50, 0x5D6B, 0x5D6E, 0x5D7C, 0x5DB2, 0x5DBA, + 0x5DE1, 0x5DE2, 0x5DFD, 0x5E28, 0x5E3D, 0x5E69, 0x5E74, 0x5EA6, + 0x5EB0, 0x5EB3, 0x5EB6, 0x5EC9, 0x5ECA, 0x5ED2, 0x5ED3, 0x5ED9, + 0x5EEC, 0x5EFE, 0x5F04, 0x5F22, 0x5F53, 0x5F62, 0x5F69, 0x5F6B, + 0x5F8B, 0x5F9A, 0x5FA9, 0x5FAD, 0x5FCD, 0x5FD7, 0x5FF5, 0x5FF9, + 0x6012, 0x601C, 0x6075, 0x6081, 0x6094, 0x60C7, 0x60D8, 0x60E1, + 0x6108, 0x6144, 0x6148, 0x614C, 0x614E, 0x6160, 0x6168, 0x617A, + 0x618E, 0x6190, 0x61A4, 0x61AF, 0x61B2, 0x61DE, 0x61F2, 0x61F6, + 0x6200, 0x6210, 0x621B, 0x622E, 0x6234, 0x625D, 0x62B1, 0x62C9, + 0x62CF, 0x62D3, 0x62D4, 0x62FC, 0x62FE, 0x633D, 0x6350, 0x6368, + 0x637B, 0x6383, 0x63A0, 0x63A9, 0x63C4, 0x63C5, 0x63E4, 0x641C, + 0x6422, 0x6452, 0x6469, 0x6477, 0x647E, 0x649A, 0x649D, 0x64C4, + 0x654F, 0x6556, 0x656C, 0x6578, 0x6599, 0x65C5, 0x65E2, 0x65E3, + 0x6613, 0x6649, 0x6674, 0x6688, 0x6691, 0x669C, 0x66B4, 0x66C6, + 0x66F4, 0x66F8, 0x6700, 0x6717, 0x671B, 0x6721, 0x674E, 0x6753, + 0x6756, 0x675E, 0x677B, 0x6785, 0x6797, 0x67F3, 0x67FA, 0x6817, + 0x681F, 0x6852, 0x6881, 0x6885, 0x688E, 0x68A8, 0x6914, 0x6942, + 0x69A3, 0x69EA, 0x6A02, 0x6A13, 0x6AA8, 0x6AD3, 0x6ADB, 0x6B04, + 0x6B21, 0x6B54, 0x6B72, 0x6B77, 0x6B79, 0x6B9F, 0x6BAE, 0x6BBA, + 0x6BBB, 0x6C4E, 0x6C67, 0x6C88, 0x6CBF, 0x6CCC, 0x6CCD, 0x6CE5, + 0x6D16, 0x6D1B, 0x6D1E, 0x6D34, 0x6D3E, 0x6D41, 0x6D69, 0x6D6A, + 0x6D77, 0x6D78, 0x6D85, 0x6DCB, 0x6DDA, 0x6DEA, 0x6DF9, 0x6E1A, + 0x6E2F, 0x6E6E, 0x6E9C, 0x6EBA, 0x6EC7, 0x6ECB, 0x6ED1, 0x6EDB, + 0x6F0F, 0x6F22, 0x6F23, 0x6F6E, 0x6FC6, 0x6FEB, 0x6FFE, 0x701B, + 0x701E, 0x7039, 0x704A, 0x7070, 0x7077, 0x707D, 0x7099, 0x70AD, + 0x70C8, 0x70D9, 0x7145, 0x7149, 0x716E, 0x719C, 0x71CE, 0x71D0, + 0x7210, 0x721B, 0x7228, 0x722B, 0x7235, 0x7250, 0x7262, 0x7280, + 0x7295, 0x72AF, 0x72C0, 0x72FC, 0x732A, 0x7375, 0x737A, 0x7387, + 0x738B, 0x73A5, 0x73B2, 0x73DE, 0x7406, 0x7409, 0x7422, 0x7447, + 0x745C, 0x7469, 0x7471, 0x7485, 0x7489, 0x7498, 0x74CA, 0x7506, + 0x7524, 0x753B, 0x753E, 0x7559, 0x7565, 0x7570, 0x75E2, 0x7610, + 0x761D, 0x761F, 0x7642, 0x7669, 0x76CA, 0x76DB, 0x76E7, 0x76F4, + 0x7701, 0x771E, 0x771F, 0x7740, 0x774A, 0x778B, 0x77A7, 0x784E, + 0x786B, 0x788C, 0x7891, 0x78CA, 0x78CC, 0x78FB, 0x792A, 0x793C, + 0x793E, 0x7948, 0x7949, 0x7950, 0x7956, 0x795D, 0x795E, 0x7965, + 0x797F, 0x798D, 0x798E, 0x798F, 0x79AE, 0x79CA, 0x79EB, 0x7A1C, + 0x7A40, 0x7A4A, 0x7A4F, 0x7A81, 0x7AB1, 0x7ACB, 0x7AEE, 0x7B20, + 0x7BC0, 0x7BC6, 0x7BC9, 0x7C3E, 0x7C60, 0x7C7B, 0x7C92, 0x7CBE, + 0x7CD2, 0x7CD6, 0x7CE3, 0x7CE7, 0x7CE8, 0x7D00, 0x7D10, 0x7D22, + 0x7D2F, 0x7D5B, 0x7D63, 0x7DA0, 0x7DBE, 0x7DC7, 0x7DF4, 0x7E02, + 0x7E09, 0x7E37, 0x7E41, 0x7E45, 0x7F3E, 0x7F72, 0x7F79, 0x7F7A, + 0x7F85, 0x7F95, 0x7F9A, 0x7FBD, 0x7FFA, 0x8001, 0x8005, 0x8046, + 0x8060, 0x806F, 0x8070, 0x807E, 0x808B, 0x80AD, 0x80B2, 0x8103, + 0x813E, 0x81D8, 0x81E8, 0x81ED, 0x8201, 0x8204, 0x8218, 0x826F, + 0x8279, 0x828B, 0x8291, 0x829D, 0x82B1, 0x82B3, 0x82BD, 0x82E5, + 0x82E6, 0x831D, 0x8323, 0x8336, 0x8352, 0x8353, 0x8363, 0x83AD, + 0x83BD, 0x83C9, 0x83CA, 0x83CC, 0x83DC, 0x83E7, 0x83EF, 0x83F1, + 0x843D, 0x8449, 0x8457, 0x84EE, 0x84F1, 0x84F3, 0x84FC, 0x8516, + 0x8564, 0x85CD, 0x85FA, 0x8606, 0x8612, 0x862D, 0x863F, 0x8650, + 0x865C, 0x8667, 0x8669, 0x8688, 0x86A9, 0x86E2, 0x870E, 0x8728, + 0x876B, 0x8779, 0x8786, 0x87BA, 0x87E1, 0x8801, 0x881F, 0x884C, + 0x8860, 0x8863, 0x88C2, 0x88CF, 0x88D7, 0x88DE, 0x88E1, 0x88F8, + 0x88FA, 0x8910, 0x8941, 0x8964, 0x8986, 0x898B, 0x8996, 0x8AA0, + 0x8AAA, 0x8ABF, 0x8ACB, 0x8AD2, 0x8AD6, 0x8AED, 0x8AF8, 0x8AFE, + 0x8B01, 0x8B39, 0x8B58, 0x8B80, 0x8B8A, 0x8C48, 0x8C55, 0x8CAB, + 0x8CC1, 0x8CC2, 0x8CC8, 0x8CD3, 0x8D08, 0x8D1B, 0x8D77, 0x8DBC, + 0x8DCB, 0x8DEF, 0x8DF0, 0x8ECA, 0x8ED4, 0x8F26, 0x8F2A, 0x8F38, + 0x8F3B, 0x8F62, 0x8F9E, 0x8FB0, 0x8FB6, 0x9023, 0x9038, 0x9072, + 0x907C, 0x908F, 0x9094, 0x90CE, 0x90DE, 0x90F1, 0x90FD, 0x9111, + 0x911B, 0x916A, 0x9199, 0x91B4, 0x91CC, 0x91CF, 0x91D1, 0x9234, + 0x9238, 0x9276, 0x927C, 0x92D7, 0x92D8, 0x9304, 0x934A, 0x93F9, + 0x9415, 0x958B, 0x95AD, 0x95B7, 0x962E, 0x964B, 0x964D, 0x9675, + 0x9678, 0x967C, 0x9686, 0x96A3, 0x96B7, 0x96B8, 0x96C3, 0x96E2, + 0x96E3, 0x96F6, 0x96F7, 0x9723, 0x9732, 0x9748, 0x9756, 0x97DB, + 0x97E0, 0x97FF, 0x980B, 0x9818, 0x9829, 0x983B, 0x985E, 0x98E2, + 0x98EF, 0x98FC, 0x9928, 0x9929, 0x99A7, 0x99C2, 0x99F1, 0x99FE, + 0x9A6A, 0x9B12, 0x9B6F, 0x9C40, 0x9C57, 0x9CFD, 0x9D67, 0x9DB4, + 0x9DFA, 0x9E1E, 0x9E7F, 0x9E97, 0x9E9F, 0x9EBB, 0x9ECE, 0x9EF9, + 0x9EFE, 0x9F05, 0x9F0F, 0x9F16, 0x9F3B, 0x9F43, 0x9F8D, 0x9F8E, + 0x9F9C, }; -static const uint16_t -_hb_ucd_dm1_p2_map[110] = +static const uint16_t _hb_ucd_dm1_p2_map[110]= { - 0x0122u, 0x051Cu, 0x0525u, 0x054Bu, 0x063Au, 0x0804u, 0x08DEu, 0x0A2Cu, - 0x0B63u, 0x14E4u, 0x16A8u, 0x16EAu, 0x19C8u, 0x1B18u, 0x1D0Bu, 0x1DE4u, - 0x1DE6u, 0x2183u, 0x219Fu, 0x2331u, 0x26D4u, 0x2844u, 0x284Au, 0x2B0Cu, - 0x2BF1u, 0x300Au, 0x32B8u, 0x335Fu, 0x3393u, 0x339Cu, 0x33C3u, 0x33D5u, - 0x346Du, 0x36A3u, 0x38A7u, 0x3A8Du, 0x3AFAu, 0x3CBCu, 0x3D1Eu, 0x3ED1u, - 0x3F5Eu, 0x3F8Eu, 0x4263u, 0x42EEu, 0x43ABu, 0x4608u, 0x4735u, 0x4814u, - 0x4C36u, 0x4C92u, 0x4FA1u, 0x4FB8u, 0x5044u, 0x50F2u, 0x50F3u, 0x5119u, - 0x5133u, 0x5249u, 0x541Du, 0x5626u, 0x569Au, 0x56C5u, 0x597Cu, 0x5AA7u, - 0x5BABu, 0x5C80u, 0x5CD0u, 0x5F86u, 0x61DAu, 0x6228u, 0x6247u, 0x62D9u, - 0x633Eu, 0x64DAu, 0x6523u, 0x65A8u, 0x67A7u, 0x67B5u, 0x6B3Cu, 0x6C36u, - 0x6CD5u, 0x6D6Bu, 0x6F2Cu, 0x6FB1u, 0x70D2u, 0x73CAu, 0x7667u, 0x78AEu, - 0x7966u, 0x7CA8u, 0x7ED3u, 0x7F2Fu, 0x85D2u, 0x85EDu, 0x872Eu, 0x8BFAu, - 0x8D77u, 0x9145u, 0x91DFu, 0x921Au, 0x940Au, 0x9496u, 0x95B6u, 0x9B30u, - 0xA0CEu, 0xA105u, 0xA20Eu, 0xA291u, 0xA392u, 0xA600u, + 0x0122, 0x051C, 0x0525, 0x054B, 0x063A, 0x0804, 0x08DE, 0x0A2C, + 0x0B63, 0x14E4, 0x16A8, 0x16EA, 0x19C8, 0x1B18, 0x1D0B, 0x1DE4, + 0x1DE6, 0x2183, 0x219F, 0x2331, 0x26D4, 0x2844, 0x284A, 0x2B0C, + 0x2BF1, 0x300A, 0x32B8, 0x335F, 0x3393, 0x339C, 0x33C3, 0x33D5, + 0x346D, 0x36A3, 0x38A7, 0x3A8D, 0x3AFA, 0x3CBC, 0x3D1E, 0x3ED1, + 0x3F5E, 0x3F8E, 0x4263, 0x42EE, 0x43AB, 0x4608, 0x4735, 0x4814, + 0x4C36, 0x4C92, 0x4FA1, 0x4FB8, 0x5044, 0x50F2, 0x50F3, 0x5119, + 0x5133, 0x5249, 0x541D, 0x5626, 0x569A, 0x56C5, 0x597C, 0x5AA7, + 0x5BAB, 0x5C80, 0x5CD0, 0x5F86, 0x61DA, 0x6228, 0x6247, 0x62D9, + 0x633E, 0x64DA, 0x6523, 0x65A8, 0x67A7, 0x67B5, 0x6B3C, 0x6C36, + 0x6CD5, 0x6D6B, 0x6F2C, 0x6FB1, 0x70D2, 0x73CA, 0x7667, 0x78AE, + 0x7966, 0x7CA8, 0x7ED3, 0x7F2F, 0x85D2, 0x85ED, 0x872E, 0x8BFA, + 0x8D77, 0x9145, 0x91DF, 0x921A, 0x940A, 0x9496, 0x95B6, 0x9B30, + 0xA0CE, 0xA105, 0xA20E, 0xA291, 0xA392, 0xA600, }; -static const uint32_t -_hb_ucd_dm2_u32_map[638] = +static const uint32_t _hb_ucd_dm2_u32_map[638]= { - HB_CODEPOINT_ENCODE3_11_7_14 (0x003Cu, 0x0338u, 0x226Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x003Du, 0x0338u, 0x2260u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x003Eu, 0x0338u, 0x226Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0300u, 0x00C0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0301u, 0x00C1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0302u, 0x00C2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0303u, 0x00C3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0304u, 0x0100u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0306u, 0x0102u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0307u, 0x0226u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0308u, 0x00C4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0309u, 0x1EA2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Au, 0x00C5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Cu, 0x01CDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Fu, 0x0200u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0311u, 0x0202u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0323u, 0x1EA0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0325u, 0x1E00u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0328u, 0x0104u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0307u, 0x1E02u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0323u, 0x1E04u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0331u, 0x1E06u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0301u, 0x0106u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0302u, 0x0108u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0307u, 0x010Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x030Cu, 0x010Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0327u, 0x00C7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0307u, 0x1E0Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x030Cu, 0x010Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0323u, 0x1E0Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0327u, 0x1E10u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x032Du, 0x1E12u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0331u, 0x1E0Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0300u, 0x00C8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0301u, 0x00C9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0302u, 0x00CAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0303u, 0x1EBCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0304u, 0x0112u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0306u, 0x0114u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0307u, 0x0116u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0308u, 0x00CBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0309u, 0x1EBAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x030Cu, 0x011Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x030Fu, 0x0204u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0311u, 0x0206u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0323u, 0x1EB8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0327u, 0x0228u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0328u, 0x0118u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x032Du, 0x1E18u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0330u, 0x1E1Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0046u, 0x0307u, 0x1E1Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0301u, 0x01F4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0302u, 0x011Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0304u, 0x1E20u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0306u, 0x011Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0307u, 0x0120u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x030Cu, 0x01E6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0327u, 0x0122u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0302u, 0x0124u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0307u, 0x1E22u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0308u, 0x1E26u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x030Cu, 0x021Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0323u, 0x1E24u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0327u, 0x1E28u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x032Eu, 0x1E2Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0300u, 0x00CCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0301u, 0x00CDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0302u, 0x00CEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0303u, 0x0128u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0304u, 0x012Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0306u, 0x012Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0307u, 0x0130u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0308u, 0x00CFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0309u, 0x1EC8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x030Cu, 0x01CFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x030Fu, 0x0208u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0311u, 0x020Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0323u, 0x1ECAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0328u, 0x012Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0330u, 0x1E2Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Au, 0x0302u, 0x0134u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0301u, 0x1E30u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x030Cu, 0x01E8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0323u, 0x1E32u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0327u, 0x0136u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0331u, 0x1E34u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0301u, 0x0139u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x030Cu, 0x013Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0323u, 0x1E36u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0327u, 0x013Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x032Du, 0x1E3Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0331u, 0x1E3Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0301u, 0x1E3Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0307u, 0x1E40u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0323u, 0x1E42u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0300u, 0x01F8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0301u, 0x0143u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0303u, 0x00D1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0307u, 0x1E44u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x030Cu, 0x0147u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0323u, 0x1E46u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0327u, 0x0145u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x032Du, 0x1E4Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0331u, 0x1E48u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0300u, 0x00D2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0301u, 0x00D3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0302u, 0x00D4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0303u, 0x00D5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0304u, 0x014Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0306u, 0x014Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0307u, 0x022Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0308u, 0x00D6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0309u, 0x1ECEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Bu, 0x0150u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Cu, 0x01D1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Fu, 0x020Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0311u, 0x020Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x031Bu, 0x01A0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0323u, 0x1ECCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0328u, 0x01EAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0050u, 0x0301u, 0x1E54u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0050u, 0x0307u, 0x1E56u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0301u, 0x0154u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0307u, 0x1E58u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x030Cu, 0x0158u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x030Fu, 0x0210u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0311u, 0x0212u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0323u, 0x1E5Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0327u, 0x0156u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0331u, 0x1E5Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0301u, 0x015Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0302u, 0x015Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0307u, 0x1E60u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x030Cu, 0x0160u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0323u, 0x1E62u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0326u, 0x0218u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0327u, 0x015Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0307u, 0x1E6Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x030Cu, 0x0164u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0323u, 0x1E6Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0326u, 0x021Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0327u, 0x0162u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x032Du, 0x1E70u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0331u, 0x1E6Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0300u, 0x00D9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0301u, 0x00DAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0302u, 0x00DBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0303u, 0x0168u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0304u, 0x016Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0306u, 0x016Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0308u, 0x00DCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0309u, 0x1EE6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Au, 0x016Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Bu, 0x0170u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Cu, 0x01D3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Fu, 0x0214u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0311u, 0x0216u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x031Bu, 0x01AFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0323u, 0x1EE4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0324u, 0x1E72u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0328u, 0x0172u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x032Du, 0x1E76u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0330u, 0x1E74u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0056u, 0x0303u, 0x1E7Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0056u, 0x0323u, 0x1E7Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0300u, 0x1E80u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0301u, 0x1E82u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0302u, 0x0174u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0307u, 0x1E86u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0308u, 0x1E84u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0323u, 0x1E88u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0058u, 0x0307u, 0x1E8Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0058u, 0x0308u, 0x1E8Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0300u, 0x1EF2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0301u, 0x00DDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0302u, 0x0176u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0303u, 0x1EF8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0304u, 0x0232u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0307u, 0x1E8Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0308u, 0x0178u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0309u, 0x1EF6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0323u, 0x1EF4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0301u, 0x0179u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0302u, 0x1E90u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0307u, 0x017Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x030Cu, 0x017Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0323u, 0x1E92u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0331u, 0x1E94u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0300u, 0x00E0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0301u, 0x00E1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0302u, 0x00E2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0303u, 0x00E3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0304u, 0x0101u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0306u, 0x0103u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0307u, 0x0227u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0308u, 0x00E4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0309u, 0x1EA3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Au, 0x00E5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Cu, 0x01CEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Fu, 0x0201u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0311u, 0x0203u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0323u, 0x1EA1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0325u, 0x1E01u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0328u, 0x0105u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0307u, 0x1E03u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0323u, 0x1E05u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0331u, 0x1E07u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0301u, 0x0107u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0302u, 0x0109u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0307u, 0x010Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x030Cu, 0x010Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0327u, 0x00E7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0307u, 0x1E0Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x030Cu, 0x010Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0323u, 0x1E0Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0327u, 0x1E11u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x032Du, 0x1E13u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0331u, 0x1E0Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0300u, 0x00E8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0301u, 0x00E9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0302u, 0x00EAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0303u, 0x1EBDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0304u, 0x0113u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0306u, 0x0115u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0307u, 0x0117u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0308u, 0x00EBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0309u, 0x1EBBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x030Cu, 0x011Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x030Fu, 0x0205u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0311u, 0x0207u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0323u, 0x1EB9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0327u, 0x0229u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0328u, 0x0119u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x032Du, 0x1E19u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0330u, 0x1E1Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0066u, 0x0307u, 0x1E1Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0301u, 0x01F5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0302u, 0x011Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0304u, 0x1E21u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0306u, 0x011Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0307u, 0x0121u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x030Cu, 0x01E7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0327u, 0x0123u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0302u, 0x0125u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0307u, 0x1E23u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0308u, 0x1E27u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x030Cu, 0x021Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0323u, 0x1E25u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0327u, 0x1E29u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x032Eu, 0x1E2Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0331u, 0x1E96u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0300u, 0x00ECu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0301u, 0x00EDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0302u, 0x00EEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0303u, 0x0129u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0304u, 0x012Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0306u, 0x012Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0308u, 0x00EFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0309u, 0x1EC9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x030Cu, 0x01D0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x030Fu, 0x0209u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0311u, 0x020Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0323u, 0x1ECBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0328u, 0x012Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0330u, 0x1E2Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Au, 0x0302u, 0x0135u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Au, 0x030Cu, 0x01F0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0301u, 0x1E31u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x030Cu, 0x01E9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0323u, 0x1E33u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0327u, 0x0137u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0331u, 0x1E35u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0301u, 0x013Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x030Cu, 0x013Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0323u, 0x1E37u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0327u, 0x013Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x032Du, 0x1E3Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0331u, 0x1E3Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0301u, 0x1E3Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0307u, 0x1E41u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0323u, 0x1E43u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0300u, 0x01F9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0301u, 0x0144u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0303u, 0x00F1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0307u, 0x1E45u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x030Cu, 0x0148u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0323u, 0x1E47u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0327u, 0x0146u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x032Du, 0x1E4Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0331u, 0x1E49u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0300u, 0x00F2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0301u, 0x00F3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0302u, 0x00F4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0303u, 0x00F5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0304u, 0x014Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0306u, 0x014Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0307u, 0x022Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0308u, 0x00F6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0309u, 0x1ECFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Bu, 0x0151u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Cu, 0x01D2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Fu, 0x020Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0311u, 0x020Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x031Bu, 0x01A1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0323u, 0x1ECDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0328u, 0x01EBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0070u, 0x0301u, 0x1E55u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0070u, 0x0307u, 0x1E57u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0301u, 0x0155u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0307u, 0x1E59u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x030Cu, 0x0159u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x030Fu, 0x0211u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0311u, 0x0213u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0323u, 0x1E5Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0327u, 0x0157u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0331u, 0x1E5Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0301u, 0x015Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0302u, 0x015Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0307u, 0x1E61u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x030Cu, 0x0161u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0323u, 0x1E63u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0326u, 0x0219u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0327u, 0x015Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0307u, 0x1E6Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0308u, 0x1E97u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x030Cu, 0x0165u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0323u, 0x1E6Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0326u, 0x021Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0327u, 0x0163u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x032Du, 0x1E71u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0331u, 0x1E6Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0300u, 0x00F9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0301u, 0x00FAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0302u, 0x00FBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0303u, 0x0169u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0304u, 0x016Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0306u, 0x016Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0308u, 0x00FCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0309u, 0x1EE7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Au, 0x016Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Bu, 0x0171u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Cu, 0x01D4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Fu, 0x0215u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0311u, 0x0217u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x031Bu, 0x01B0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0323u, 0x1EE5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0324u, 0x1E73u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0328u, 0x0173u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x032Du, 0x1E77u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0330u, 0x1E75u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0076u, 0x0303u, 0x1E7Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0076u, 0x0323u, 0x1E7Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0300u, 0x1E81u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0301u, 0x1E83u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0302u, 0x0175u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0307u, 0x1E87u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0308u, 0x1E85u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x030Au, 0x1E98u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0323u, 0x1E89u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0078u, 0x0307u, 0x1E8Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0078u, 0x0308u, 0x1E8Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0300u, 0x1EF3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0301u, 0x00FDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0302u, 0x0177u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0303u, 0x1EF9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0304u, 0x0233u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0307u, 0x1E8Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0308u, 0x00FFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0309u, 0x1EF7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x030Au, 0x1E99u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0323u, 0x1EF5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0301u, 0x017Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0302u, 0x1E91u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0307u, 0x017Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x030Cu, 0x017Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0323u, 0x1E93u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0331u, 0x1E95u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0300u, 0x1FEDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0301u, 0x0385u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0342u, 0x1FC1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0300u, 0x1EA6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0301u, 0x1EA4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0303u, 0x1EAAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0309u, 0x1EA8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C4u, 0x0304u, 0x01DEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C5u, 0x0301u, 0x01FAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6u, 0x0301u, 0x01FCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6u, 0x0304u, 0x01E2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C7u, 0x0301u, 0x1E08u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0300u, 0x1EC0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0301u, 0x1EBEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0303u, 0x1EC4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0309u, 0x1EC2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00CFu, 0x0301u, 0x1E2Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0300u, 0x1ED2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0301u, 0x1ED0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0303u, 0x1ED6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0309u, 0x1ED4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0301u, 0x1E4Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0304u, 0x022Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0308u, 0x1E4Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D6u, 0x0304u, 0x022Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D8u, 0x0301u, 0x01FEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0300u, 0x01DBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0301u, 0x01D7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0304u, 0x01D5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x030Cu, 0x01D9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0300u, 0x1EA7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0301u, 0x1EA5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0303u, 0x1EABu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0309u, 0x1EA9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E4u, 0x0304u, 0x01DFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E5u, 0x0301u, 0x01FBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6u, 0x0301u, 0x01FDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6u, 0x0304u, 0x01E3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E7u, 0x0301u, 0x1E09u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0300u, 0x1EC1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0301u, 0x1EBFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0303u, 0x1EC5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0309u, 0x1EC3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00EFu, 0x0301u, 0x1E2Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0300u, 0x1ED3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0301u, 0x1ED1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0303u, 0x1ED7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0309u, 0x1ED5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0301u, 0x1E4Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0304u, 0x022Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0308u, 0x1E4Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F6u, 0x0304u, 0x022Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F8u, 0x0301u, 0x01FFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0300u, 0x01DCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0301u, 0x01D8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0304u, 0x01D6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x030Cu, 0x01DAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0300u, 0x1EB0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0301u, 0x1EAEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0303u, 0x1EB4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0309u, 0x1EB2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0300u, 0x1EB1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0301u, 0x1EAFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0303u, 0x1EB5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0309u, 0x1EB3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0112u, 0x0300u, 0x1E14u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0112u, 0x0301u, 0x1E16u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0113u, 0x0300u, 0x1E15u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0113u, 0x0301u, 0x1E17u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x014Cu, 0x0300u, 0x1E50u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x014Cu, 0x0301u, 0x1E52u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x014Du, 0x0300u, 0x1E51u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x014Du, 0x0301u, 0x1E53u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x015Au, 0x0307u, 0x1E64u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x015Bu, 0x0307u, 0x1E65u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0160u, 0x0307u, 0x1E66u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0161u, 0x0307u, 0x1E67u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0168u, 0x0301u, 0x1E78u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0169u, 0x0301u, 0x1E79u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x016Au, 0x0308u, 0x1E7Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x016Bu, 0x0308u, 0x1E7Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x017Fu, 0x0307u, 0x1E9Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0300u, 0x1EDCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0301u, 0x1EDAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0303u, 0x1EE0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0309u, 0x1EDEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0323u, 0x1EE2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0300u, 0x1EDDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0301u, 0x1EDBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0303u, 0x1EE1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0309u, 0x1EDFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0323u, 0x1EE3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0300u, 0x1EEAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0301u, 0x1EE8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0303u, 0x1EEEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0309u, 0x1EECu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0323u, 0x1EF0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0300u, 0x1EEBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0301u, 0x1EE9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0303u, 0x1EEFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0309u, 0x1EEDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0323u, 0x1EF1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01B7u, 0x030Cu, 0x01EEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01EAu, 0x0304u, 0x01ECu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01EBu, 0x0304u, 0x01EDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0226u, 0x0304u, 0x01E0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0227u, 0x0304u, 0x01E1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0228u, 0x0306u, 0x1E1Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0229u, 0x0306u, 0x1E1Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x022Eu, 0x0304u, 0x0230u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x022Fu, 0x0304u, 0x0231u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0292u, 0x030Cu, 0x01EFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0308u, 0x0301u, 0x0000u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0300u, 0x1FBAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0301u, 0x0386u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0304u, 0x1FB9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0306u, 0x1FB8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0313u, 0x1F08u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0314u, 0x1F09u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0345u, 0x1FBCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0300u, 0x1FC8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0301u, 0x0388u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0313u, 0x1F18u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0314u, 0x1F19u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0300u, 0x1FCAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0301u, 0x0389u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0313u, 0x1F28u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0314u, 0x1F29u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0345u, 0x1FCCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0300u, 0x1FDAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0301u, 0x038Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0304u, 0x1FD9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0306u, 0x1FD8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0308u, 0x03AAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0313u, 0x1F38u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0314u, 0x1F39u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0300u, 0x1FF8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0301u, 0x038Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0313u, 0x1F48u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0314u, 0x1F49u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A1u, 0x0314u, 0x1FECu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0300u, 0x1FEAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0301u, 0x038Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0304u, 0x1FE9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0306u, 0x1FE8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0308u, 0x03ABu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0314u, 0x1F59u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0300u, 0x1FFAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0301u, 0x038Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0313u, 0x1F68u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0314u, 0x1F69u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0345u, 0x1FFCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03ACu, 0x0345u, 0x1FB4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03AEu, 0x0345u, 0x1FC4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0300u, 0x1F70u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0301u, 0x03ACu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0304u, 0x1FB1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0306u, 0x1FB0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0313u, 0x1F00u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0314u, 0x1F01u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0342u, 0x1FB6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0345u, 0x1FB3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0300u, 0x1F72u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0301u, 0x03ADu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0313u, 0x1F10u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0314u, 0x1F11u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0300u, 0x1F74u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0301u, 0x03AEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0313u, 0x1F20u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0314u, 0x1F21u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0342u, 0x1FC6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0345u, 0x1FC3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0300u, 0x1F76u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0301u, 0x03AFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0304u, 0x1FD1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0306u, 0x1FD0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0308u, 0x03CAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0313u, 0x1F30u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0314u, 0x1F31u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0342u, 0x1FD6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0300u, 0x1F78u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0301u, 0x03CCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0313u, 0x1F40u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0314u, 0x1F41u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1u, 0x0313u, 0x1FE4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1u, 0x0314u, 0x1FE5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0300u, 0x1F7Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0301u, 0x03CDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0304u, 0x1FE1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0306u, 0x1FE0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0308u, 0x03CBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0313u, 0x1F50u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0314u, 0x1F51u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0342u, 0x1FE6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0300u, 0x1F7Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0301u, 0x03CEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0313u, 0x1F60u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0314u, 0x1F61u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0342u, 0x1FF6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0345u, 0x1FF3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0300u, 0x1FD2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0301u, 0x0390u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0342u, 0x1FD7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0300u, 0x1FE2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0301u, 0x03B0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0342u, 0x1FE7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03CEu, 0x0345u, 0x1FF4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2u, 0x0301u, 0x03D3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2u, 0x0308u, 0x03D4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0406u, 0x0308u, 0x0407u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0410u, 0x0306u, 0x04D0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0410u, 0x0308u, 0x04D2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0413u, 0x0301u, 0x0403u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0300u, 0x0400u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0306u, 0x04D6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0308u, 0x0401u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0416u, 0x0306u, 0x04C1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0416u, 0x0308u, 0x04DCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0417u, 0x0308u, 0x04DEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0300u, 0x040Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0304u, 0x04E2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0306u, 0x0419u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0308u, 0x04E4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x041Au, 0x0301u, 0x040Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x041Eu, 0x0308u, 0x04E6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0304u, 0x04EEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0306u, 0x040Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0308u, 0x04F0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x030Bu, 0x04F2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0427u, 0x0308u, 0x04F4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x042Bu, 0x0308u, 0x04F8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x042Du, 0x0308u, 0x04ECu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0430u, 0x0306u, 0x04D1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0430u, 0x0308u, 0x04D3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0433u, 0x0301u, 0x0453u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0300u, 0x0450u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0306u, 0x04D7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0308u, 0x0451u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0436u, 0x0306u, 0x04C2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0436u, 0x0308u, 0x04DDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0437u, 0x0308u, 0x04DFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0300u, 0x045Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0304u, 0x04E3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0306u, 0x0439u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0308u, 0x04E5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x043Au, 0x0301u, 0x045Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x043Eu, 0x0308u, 0x04E7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0304u, 0x04EFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0306u, 0x045Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0308u, 0x04F1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x030Bu, 0x04F3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0447u, 0x0308u, 0x04F5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x044Bu, 0x0308u, 0x04F9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x044Du, 0x0308u, 0x04EDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0456u, 0x0308u, 0x0457u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0474u, 0x030Fu, 0x0476u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0475u, 0x030Fu, 0x0477u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x04D8u, 0x0308u, 0x04DAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x04D9u, 0x0308u, 0x04DBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x04E8u, 0x0308u, 0x04EAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x04E9u, 0x0308u, 0x04EBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x003C, 0x0338, 0x226E),HB_CODEPOINT_ENCODE3_11_7_14 (0x003D, 0x0338, 0x2260), + HB_CODEPOINT_ENCODE3_11_7_14 (0x003E, 0x0338, 0x226F),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0300, 0x00C0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0301, 0x00C1),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0302, 0x00C2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0303, 0x00C3),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0304, 0x0100), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0306, 0x0102),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0307, 0x0226), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0308, 0x00C4),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0309, 0x1EA2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x030A, 0x00C5),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x030C, 0x01CD), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x030F, 0x0200),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0311, 0x0202), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0323, 0x1EA0),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0325, 0x1E00), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0328, 0x0104),HB_CODEPOINT_ENCODE3_11_7_14 (0x0042, 0x0307, 0x1E02), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0042, 0x0323, 0x1E04),HB_CODEPOINT_ENCODE3_11_7_14 (0x0042, 0x0331, 0x1E06), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043, 0x0301, 0x0106),HB_CODEPOINT_ENCODE3_11_7_14 (0x0043, 0x0302, 0x0108), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043, 0x0307, 0x010A),HB_CODEPOINT_ENCODE3_11_7_14 (0x0043, 0x030C, 0x010C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043, 0x0327, 0x00C7),HB_CODEPOINT_ENCODE3_11_7_14 (0x0044, 0x0307, 0x1E0A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044, 0x030C, 0x010E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0044, 0x0323, 0x1E0C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044, 0x0327, 0x1E10),HB_CODEPOINT_ENCODE3_11_7_14 (0x0044, 0x032D, 0x1E12), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044, 0x0331, 0x1E0E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0300, 0x00C8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0301, 0x00C9),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0302, 0x00CA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0303, 0x1EBC),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0304, 0x0112), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0306, 0x0114),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0307, 0x0116), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0308, 0x00CB),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0309, 0x1EBA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x030C, 0x011A),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x030F, 0x0204), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0311, 0x0206),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0323, 0x1EB8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0327, 0x0228),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0328, 0x0118), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x032D, 0x1E18),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0330, 0x1E1A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0046, 0x0307, 0x1E1E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0047, 0x0301, 0x01F4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047, 0x0302, 0x011C),HB_CODEPOINT_ENCODE3_11_7_14 (0x0047, 0x0304, 0x1E20), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047, 0x0306, 0x011E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0047, 0x0307, 0x0120), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047, 0x030C, 0x01E6),HB_CODEPOINT_ENCODE3_11_7_14 (0x0047, 0x0327, 0x0122), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048, 0x0302, 0x0124),HB_CODEPOINT_ENCODE3_11_7_14 (0x0048, 0x0307, 0x1E22), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048, 0x0308, 0x1E26),HB_CODEPOINT_ENCODE3_11_7_14 (0x0048, 0x030C, 0x021E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048, 0x0323, 0x1E24),HB_CODEPOINT_ENCODE3_11_7_14 (0x0048, 0x0327, 0x1E28), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048, 0x032E, 0x1E2A),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0300, 0x00CC), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0301, 0x00CD),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0302, 0x00CE), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0303, 0x0128),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0304, 0x012A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0306, 0x012C),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0307, 0x0130), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0308, 0x00CF),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0309, 0x1EC8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x030C, 0x01CF),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x030F, 0x0208), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0311, 0x020A),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0323, 0x1ECA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0328, 0x012E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0330, 0x1E2C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004A, 0x0302, 0x0134),HB_CODEPOINT_ENCODE3_11_7_14 (0x004B, 0x0301, 0x1E30), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004B, 0x030C, 0x01E8),HB_CODEPOINT_ENCODE3_11_7_14 (0x004B, 0x0323, 0x1E32), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004B, 0x0327, 0x0136),HB_CODEPOINT_ENCODE3_11_7_14 (0x004B, 0x0331, 0x1E34), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004C, 0x0301, 0x0139),HB_CODEPOINT_ENCODE3_11_7_14 (0x004C, 0x030C, 0x013D), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004C, 0x0323, 0x1E36),HB_CODEPOINT_ENCODE3_11_7_14 (0x004C, 0x0327, 0x013B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004C, 0x032D, 0x1E3C),HB_CODEPOINT_ENCODE3_11_7_14 (0x004C, 0x0331, 0x1E3A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004D, 0x0301, 0x1E3E),HB_CODEPOINT_ENCODE3_11_7_14 (0x004D, 0x0307, 0x1E40), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004D, 0x0323, 0x1E42),HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x0300, 0x01F8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x0301, 0x0143),HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x0303, 0x00D1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x0307, 0x1E44),HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x030C, 0x0147), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x0323, 0x1E46),HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x0327, 0x0145), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x032D, 0x1E4A),HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x0331, 0x1E48), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0300, 0x00D2),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0301, 0x00D3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0302, 0x00D4),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0303, 0x00D5), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0304, 0x014C),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0306, 0x014E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0307, 0x022E),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0308, 0x00D6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0309, 0x1ECE),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x030B, 0x0150), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x030C, 0x01D1),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x030F, 0x020C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0311, 0x020E),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x031B, 0x01A0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0323, 0x1ECC),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0328, 0x01EA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0050, 0x0301, 0x1E54),HB_CODEPOINT_ENCODE3_11_7_14 (0x0050, 0x0307, 0x1E56), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x0301, 0x0154),HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x0307, 0x1E58), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x030C, 0x0158),HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x030F, 0x0210), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x0311, 0x0212),HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x0323, 0x1E5A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x0327, 0x0156),HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x0331, 0x1E5E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053, 0x0301, 0x015A),HB_CODEPOINT_ENCODE3_11_7_14 (0x0053, 0x0302, 0x015C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053, 0x0307, 0x1E60),HB_CODEPOINT_ENCODE3_11_7_14 (0x0053, 0x030C, 0x0160), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053, 0x0323, 0x1E62),HB_CODEPOINT_ENCODE3_11_7_14 (0x0053, 0x0326, 0x0218), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053, 0x0327, 0x015E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0054, 0x0307, 0x1E6A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054, 0x030C, 0x0164),HB_CODEPOINT_ENCODE3_11_7_14 (0x0054, 0x0323, 0x1E6C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054, 0x0326, 0x021A),HB_CODEPOINT_ENCODE3_11_7_14 (0x0054, 0x0327, 0x0162), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054, 0x032D, 0x1E70),HB_CODEPOINT_ENCODE3_11_7_14 (0x0054, 0x0331, 0x1E6E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0300, 0x00D9),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0301, 0x00DA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0302, 0x00DB),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0303, 0x0168), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0304, 0x016A),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0306, 0x016C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0308, 0x00DC),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0309, 0x1EE6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x030A, 0x016E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x030B, 0x0170), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x030C, 0x01D3),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x030F, 0x0214), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0311, 0x0216),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x031B, 0x01AF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0323, 0x1EE4),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0324, 0x1E72), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0328, 0x0172),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x032D, 0x1E76), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0330, 0x1E74),HB_CODEPOINT_ENCODE3_11_7_14 (0x0056, 0x0303, 0x1E7C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0056, 0x0323, 0x1E7E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0057, 0x0300, 0x1E80), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057, 0x0301, 0x1E82),HB_CODEPOINT_ENCODE3_11_7_14 (0x0057, 0x0302, 0x0174), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057, 0x0307, 0x1E86),HB_CODEPOINT_ENCODE3_11_7_14 (0x0057, 0x0308, 0x1E84), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057, 0x0323, 0x1E88),HB_CODEPOINT_ENCODE3_11_7_14 (0x0058, 0x0307, 0x1E8A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0058, 0x0308, 0x1E8C),HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0300, 0x1EF2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0301, 0x00DD),HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0302, 0x0176), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0303, 0x1EF8),HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0304, 0x0232), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0307, 0x1E8E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0308, 0x0178), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0309, 0x1EF6),HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0323, 0x1EF4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005A, 0x0301, 0x0179),HB_CODEPOINT_ENCODE3_11_7_14 (0x005A, 0x0302, 0x1E90), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005A, 0x0307, 0x017B),HB_CODEPOINT_ENCODE3_11_7_14 (0x005A, 0x030C, 0x017D), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005A, 0x0323, 0x1E92),HB_CODEPOINT_ENCODE3_11_7_14 (0x005A, 0x0331, 0x1E94), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0300, 0x00E0),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0301, 0x00E1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0302, 0x00E2),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0303, 0x00E3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0304, 0x0101),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0306, 0x0103), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0307, 0x0227),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0308, 0x00E4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0309, 0x1EA3),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x030A, 0x00E5), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x030C, 0x01CE),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x030F, 0x0201), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0311, 0x0203),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0323, 0x1EA1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0325, 0x1E01),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0328, 0x0105), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062, 0x0307, 0x1E03),HB_CODEPOINT_ENCODE3_11_7_14 (0x0062, 0x0323, 0x1E05), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062, 0x0331, 0x1E07),HB_CODEPOINT_ENCODE3_11_7_14 (0x0063, 0x0301, 0x0107), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063, 0x0302, 0x0109),HB_CODEPOINT_ENCODE3_11_7_14 (0x0063, 0x0307, 0x010B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063, 0x030C, 0x010D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0063, 0x0327, 0x00E7), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064, 0x0307, 0x1E0B),HB_CODEPOINT_ENCODE3_11_7_14 (0x0064, 0x030C, 0x010F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064, 0x0323, 0x1E0D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0064, 0x0327, 0x1E11), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064, 0x032D, 0x1E13),HB_CODEPOINT_ENCODE3_11_7_14 (0x0064, 0x0331, 0x1E0F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0300, 0x00E8),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0301, 0x00E9), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0302, 0x00EA),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0303, 0x1EBD), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0304, 0x0113),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0306, 0x0115), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0307, 0x0117),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0308, 0x00EB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0309, 0x1EBB),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x030C, 0x011B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x030F, 0x0205),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0311, 0x0207), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0323, 0x1EB9),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0327, 0x0229), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0328, 0x0119),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x032D, 0x1E19), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0330, 0x1E1B),HB_CODEPOINT_ENCODE3_11_7_14 (0x0066, 0x0307, 0x1E1F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067, 0x0301, 0x01F5),HB_CODEPOINT_ENCODE3_11_7_14 (0x0067, 0x0302, 0x011D), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067, 0x0304, 0x1E21),HB_CODEPOINT_ENCODE3_11_7_14 (0x0067, 0x0306, 0x011F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067, 0x0307, 0x0121),HB_CODEPOINT_ENCODE3_11_7_14 (0x0067, 0x030C, 0x01E7), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067, 0x0327, 0x0123),HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x0302, 0x0125), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x0307, 0x1E23),HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x0308, 0x1E27), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x030C, 0x021F),HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x0323, 0x1E25), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x0327, 0x1E29),HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x032E, 0x1E2B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x0331, 0x1E96),HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0300, 0x00EC), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0301, 0x00ED),HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0302, 0x00EE), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0303, 0x0129),HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0304, 0x012B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0306, 0x012D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0308, 0x00EF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0309, 0x1EC9),HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x030C, 0x01D0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x030F, 0x0209),HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0311, 0x020B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0323, 0x1ECB),HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0328, 0x012F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0330, 0x1E2D),HB_CODEPOINT_ENCODE3_11_7_14 (0x006A, 0x0302, 0x0135), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006A, 0x030C, 0x01F0),HB_CODEPOINT_ENCODE3_11_7_14 (0x006B, 0x0301, 0x1E31), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006B, 0x030C, 0x01E9),HB_CODEPOINT_ENCODE3_11_7_14 (0x006B, 0x0323, 0x1E33), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006B, 0x0327, 0x0137),HB_CODEPOINT_ENCODE3_11_7_14 (0x006B, 0x0331, 0x1E35), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006C, 0x0301, 0x013A),HB_CODEPOINT_ENCODE3_11_7_14 (0x006C, 0x030C, 0x013E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006C, 0x0323, 0x1E37),HB_CODEPOINT_ENCODE3_11_7_14 (0x006C, 0x0327, 0x013C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006C, 0x032D, 0x1E3D),HB_CODEPOINT_ENCODE3_11_7_14 (0x006C, 0x0331, 0x1E3B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006D, 0x0301, 0x1E3F),HB_CODEPOINT_ENCODE3_11_7_14 (0x006D, 0x0307, 0x1E41), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006D, 0x0323, 0x1E43),HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x0300, 0x01F9), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x0301, 0x0144),HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x0303, 0x00F1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x0307, 0x1E45),HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x030C, 0x0148), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x0323, 0x1E47),HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x0327, 0x0146), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x032D, 0x1E4B),HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x0331, 0x1E49), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0300, 0x00F2),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0301, 0x00F3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0302, 0x00F4),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0303, 0x00F5), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0304, 0x014D),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0306, 0x014F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0307, 0x022F),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0308, 0x00F6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0309, 0x1ECF),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x030B, 0x0151), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x030C, 0x01D2),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x030F, 0x020D), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0311, 0x020F),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x031B, 0x01A1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0323, 0x1ECD),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0328, 0x01EB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0070, 0x0301, 0x1E55),HB_CODEPOINT_ENCODE3_11_7_14 (0x0070, 0x0307, 0x1E57), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x0301, 0x0155),HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x0307, 0x1E59), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x030C, 0x0159),HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x030F, 0x0211), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x0311, 0x0213),HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x0323, 0x1E5B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x0327, 0x0157),HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x0331, 0x1E5F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073, 0x0301, 0x015B),HB_CODEPOINT_ENCODE3_11_7_14 (0x0073, 0x0302, 0x015D), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073, 0x0307, 0x1E61),HB_CODEPOINT_ENCODE3_11_7_14 (0x0073, 0x030C, 0x0161), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073, 0x0323, 0x1E63),HB_CODEPOINT_ENCODE3_11_7_14 (0x0073, 0x0326, 0x0219), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073, 0x0327, 0x015F),HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x0307, 0x1E6B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x0308, 0x1E97),HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x030C, 0x0165), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x0323, 0x1E6D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x0326, 0x021B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x0327, 0x0163),HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x032D, 0x1E71), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x0331, 0x1E6F),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0300, 0x00F9), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0301, 0x00FA),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0302, 0x00FB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0303, 0x0169),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0304, 0x016B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0306, 0x016D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0308, 0x00FC), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0309, 0x1EE7),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x030A, 0x016F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x030B, 0x0171),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x030C, 0x01D4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x030F, 0x0215),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0311, 0x0217), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x031B, 0x01B0),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0323, 0x1EE5), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0324, 0x1E73),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0328, 0x0173), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x032D, 0x1E77),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0330, 0x1E75), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0076, 0x0303, 0x1E7D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0076, 0x0323, 0x1E7F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077, 0x0300, 0x1E81),HB_CODEPOINT_ENCODE3_11_7_14 (0x0077, 0x0301, 0x1E83), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077, 0x0302, 0x0175),HB_CODEPOINT_ENCODE3_11_7_14 (0x0077, 0x0307, 0x1E87), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077, 0x0308, 0x1E85),HB_CODEPOINT_ENCODE3_11_7_14 (0x0077, 0x030A, 0x1E98), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077, 0x0323, 0x1E89),HB_CODEPOINT_ENCODE3_11_7_14 (0x0078, 0x0307, 0x1E8B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0078, 0x0308, 0x1E8D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0300, 0x1EF3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0301, 0x00FD),HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0302, 0x0177), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0303, 0x1EF9),HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0304, 0x0233), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0307, 0x1E8F),HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0308, 0x00FF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0309, 0x1EF7),HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x030A, 0x1E99), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0323, 0x1EF5),HB_CODEPOINT_ENCODE3_11_7_14 (0x007A, 0x0301, 0x017A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007A, 0x0302, 0x1E91),HB_CODEPOINT_ENCODE3_11_7_14 (0x007A, 0x0307, 0x017C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007A, 0x030C, 0x017E),HB_CODEPOINT_ENCODE3_11_7_14 (0x007A, 0x0323, 0x1E93), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007A, 0x0331, 0x1E95),HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8, 0x0300, 0x1FED), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8, 0x0301, 0x0385),HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8, 0x0342, 0x1FC1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2, 0x0300, 0x1EA6),HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2, 0x0301, 0x1EA4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2, 0x0303, 0x1EAA),HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2, 0x0309, 0x1EA8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C4, 0x0304, 0x01DE),HB_CODEPOINT_ENCODE3_11_7_14 (0x00C5, 0x0301, 0x01FA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6, 0x0301, 0x01FC),HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6, 0x0304, 0x01E2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C7, 0x0301, 0x1E08),HB_CODEPOINT_ENCODE3_11_7_14 (0x00CA, 0x0300, 0x1EC0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CA, 0x0301, 0x1EBE),HB_CODEPOINT_ENCODE3_11_7_14 (0x00CA, 0x0303, 0x1EC4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CA, 0x0309, 0x1EC2),HB_CODEPOINT_ENCODE3_11_7_14 (0x00CF, 0x0301, 0x1E2E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4, 0x0300, 0x1ED2),HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4, 0x0301, 0x1ED0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4, 0x0303, 0x1ED6),HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4, 0x0309, 0x1ED4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5, 0x0301, 0x1E4C),HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5, 0x0304, 0x022C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5, 0x0308, 0x1E4E),HB_CODEPOINT_ENCODE3_11_7_14 (0x00D6, 0x0304, 0x022A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D8, 0x0301, 0x01FE),HB_CODEPOINT_ENCODE3_11_7_14 (0x00DC, 0x0300, 0x01DB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DC, 0x0301, 0x01D7),HB_CODEPOINT_ENCODE3_11_7_14 (0x00DC, 0x0304, 0x01D5), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DC, 0x030C, 0x01D9),HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2, 0x0300, 0x1EA7), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2, 0x0301, 0x1EA5),HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2, 0x0303, 0x1EAB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2, 0x0309, 0x1EA9),HB_CODEPOINT_ENCODE3_11_7_14 (0x00E4, 0x0304, 0x01DF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E5, 0x0301, 0x01FB),HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6, 0x0301, 0x01FD), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6, 0x0304, 0x01E3),HB_CODEPOINT_ENCODE3_11_7_14 (0x00E7, 0x0301, 0x1E09), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EA, 0x0300, 0x1EC1),HB_CODEPOINT_ENCODE3_11_7_14 (0x00EA, 0x0301, 0x1EBF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EA, 0x0303, 0x1EC5),HB_CODEPOINT_ENCODE3_11_7_14 (0x00EA, 0x0309, 0x1EC3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EF, 0x0301, 0x1E2F),HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4, 0x0300, 0x1ED3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4, 0x0301, 0x1ED1),HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4, 0x0303, 0x1ED7), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4, 0x0309, 0x1ED5),HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5, 0x0301, 0x1E4D), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5, 0x0304, 0x022D),HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5, 0x0308, 0x1E4F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F6, 0x0304, 0x022B),HB_CODEPOINT_ENCODE3_11_7_14 (0x00F8, 0x0301, 0x01FF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FC, 0x0300, 0x01DC),HB_CODEPOINT_ENCODE3_11_7_14 (0x00FC, 0x0301, 0x01D8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FC, 0x0304, 0x01D6),HB_CODEPOINT_ENCODE3_11_7_14 (0x00FC, 0x030C, 0x01DA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102, 0x0300, 0x1EB0),HB_CODEPOINT_ENCODE3_11_7_14 (0x0102, 0x0301, 0x1EAE), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102, 0x0303, 0x1EB4),HB_CODEPOINT_ENCODE3_11_7_14 (0x0102, 0x0309, 0x1EB2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103, 0x0300, 0x1EB1),HB_CODEPOINT_ENCODE3_11_7_14 (0x0103, 0x0301, 0x1EAF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103, 0x0303, 0x1EB5),HB_CODEPOINT_ENCODE3_11_7_14 (0x0103, 0x0309, 0x1EB3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0112, 0x0300, 0x1E14),HB_CODEPOINT_ENCODE3_11_7_14 (0x0112, 0x0301, 0x1E16), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0113, 0x0300, 0x1E15),HB_CODEPOINT_ENCODE3_11_7_14 (0x0113, 0x0301, 0x1E17), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014C, 0x0300, 0x1E50),HB_CODEPOINT_ENCODE3_11_7_14 (0x014C, 0x0301, 0x1E52), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014D, 0x0300, 0x1E51),HB_CODEPOINT_ENCODE3_11_7_14 (0x014D, 0x0301, 0x1E53), + HB_CODEPOINT_ENCODE3_11_7_14 (0x015A, 0x0307, 0x1E64),HB_CODEPOINT_ENCODE3_11_7_14 (0x015B, 0x0307, 0x1E65), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0160, 0x0307, 0x1E66),HB_CODEPOINT_ENCODE3_11_7_14 (0x0161, 0x0307, 0x1E67), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0168, 0x0301, 0x1E78),HB_CODEPOINT_ENCODE3_11_7_14 (0x0169, 0x0301, 0x1E79), + HB_CODEPOINT_ENCODE3_11_7_14 (0x016A, 0x0308, 0x1E7A),HB_CODEPOINT_ENCODE3_11_7_14 (0x016B, 0x0308, 0x1E7B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x017F, 0x0307, 0x1E9B),HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0, 0x0300, 0x1EDC), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0, 0x0301, 0x1EDA),HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0, 0x0303, 0x1EE0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0, 0x0309, 0x1EDE),HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0, 0x0323, 0x1EE2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1, 0x0300, 0x1EDD),HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1, 0x0301, 0x1EDB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1, 0x0303, 0x1EE1),HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1, 0x0309, 0x1EDF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1, 0x0323, 0x1EE3),HB_CODEPOINT_ENCODE3_11_7_14 (0x01AF, 0x0300, 0x1EEA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AF, 0x0301, 0x1EE8),HB_CODEPOINT_ENCODE3_11_7_14 (0x01AF, 0x0303, 0x1EEE), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AF, 0x0309, 0x1EEC),HB_CODEPOINT_ENCODE3_11_7_14 (0x01AF, 0x0323, 0x1EF0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0, 0x0300, 0x1EEB),HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0, 0x0301, 0x1EE9), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0, 0x0303, 0x1EEF),HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0, 0x0309, 0x1EED), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0, 0x0323, 0x1EF1),HB_CODEPOINT_ENCODE3_11_7_14 (0x01B7, 0x030C, 0x01EE), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01EA, 0x0304, 0x01EC),HB_CODEPOINT_ENCODE3_11_7_14 (0x01EB, 0x0304, 0x01ED), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0226, 0x0304, 0x01E0),HB_CODEPOINT_ENCODE3_11_7_14 (0x0227, 0x0304, 0x01E1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0228, 0x0306, 0x1E1C),HB_CODEPOINT_ENCODE3_11_7_14 (0x0229, 0x0306, 0x1E1D), + HB_CODEPOINT_ENCODE3_11_7_14 (0x022E, 0x0304, 0x0230),HB_CODEPOINT_ENCODE3_11_7_14 (0x022F, 0x0304, 0x0231), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0292, 0x030C, 0x01EF),HB_CODEPOINT_ENCODE3_11_7_14 (0x0308, 0x0301, 0x0000), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391, 0x0300, 0x1FBA),HB_CODEPOINT_ENCODE3_11_7_14 (0x0391, 0x0301, 0x0386), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391, 0x0304, 0x1FB9),HB_CODEPOINT_ENCODE3_11_7_14 (0x0391, 0x0306, 0x1FB8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391, 0x0313, 0x1F08),HB_CODEPOINT_ENCODE3_11_7_14 (0x0391, 0x0314, 0x1F09), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391, 0x0345, 0x1FBC),HB_CODEPOINT_ENCODE3_11_7_14 (0x0395, 0x0300, 0x1FC8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395, 0x0301, 0x0388),HB_CODEPOINT_ENCODE3_11_7_14 (0x0395, 0x0313, 0x1F18), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395, 0x0314, 0x1F19),HB_CODEPOINT_ENCODE3_11_7_14 (0x0397, 0x0300, 0x1FCA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397, 0x0301, 0x0389),HB_CODEPOINT_ENCODE3_11_7_14 (0x0397, 0x0313, 0x1F28), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397, 0x0314, 0x1F29),HB_CODEPOINT_ENCODE3_11_7_14 (0x0397, 0x0345, 0x1FCC), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399, 0x0300, 0x1FDA),HB_CODEPOINT_ENCODE3_11_7_14 (0x0399, 0x0301, 0x038A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399, 0x0304, 0x1FD9),HB_CODEPOINT_ENCODE3_11_7_14 (0x0399, 0x0306, 0x1FD8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399, 0x0308, 0x03AA),HB_CODEPOINT_ENCODE3_11_7_14 (0x0399, 0x0313, 0x1F38), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399, 0x0314, 0x1F39),HB_CODEPOINT_ENCODE3_11_7_14 (0x039F, 0x0300, 0x1FF8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039F, 0x0301, 0x038C),HB_CODEPOINT_ENCODE3_11_7_14 (0x039F, 0x0313, 0x1F48), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039F, 0x0314, 0x1F49),HB_CODEPOINT_ENCODE3_11_7_14 (0x03A1, 0x0314, 0x1FEC), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5, 0x0300, 0x1FEA),HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5, 0x0301, 0x038E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5, 0x0304, 0x1FE9),HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5, 0x0306, 0x1FE8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5, 0x0308, 0x03AB),HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5, 0x0314, 0x1F59), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9, 0x0300, 0x1FFA),HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9, 0x0301, 0x038F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9, 0x0313, 0x1F68),HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9, 0x0314, 0x1F69), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9, 0x0345, 0x1FFC),HB_CODEPOINT_ENCODE3_11_7_14 (0x03AC, 0x0345, 0x1FB4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03AE, 0x0345, 0x1FC4),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0300, 0x1F70), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0301, 0x03AC),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0304, 0x1FB1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0306, 0x1FB0),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0313, 0x1F00), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0314, 0x1F01),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0342, 0x1FB6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0345, 0x1FB3),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5, 0x0300, 0x1F72), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5, 0x0301, 0x03AD),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5, 0x0313, 0x1F10), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5, 0x0314, 0x1F11),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7, 0x0300, 0x1F74), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7, 0x0301, 0x03AE),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7, 0x0313, 0x1F20), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7, 0x0314, 0x1F21),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7, 0x0342, 0x1FC6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7, 0x0345, 0x1FC3),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0300, 0x1F76), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0301, 0x03AF),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0304, 0x1FD1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0306, 0x1FD0),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0308, 0x03CA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0313, 0x1F30),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0314, 0x1F31), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0342, 0x1FD6),HB_CODEPOINT_ENCODE3_11_7_14 (0x03BF, 0x0300, 0x1F78), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BF, 0x0301, 0x03CC),HB_CODEPOINT_ENCODE3_11_7_14 (0x03BF, 0x0313, 0x1F40), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BF, 0x0314, 0x1F41),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1, 0x0313, 0x1FE4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1, 0x0314, 0x1FE5),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0300, 0x1F7A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0301, 0x03CD),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0304, 0x1FE1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0306, 0x1FE0),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0308, 0x03CB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0313, 0x1F50),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0314, 0x1F51), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0342, 0x1FE6),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9, 0x0300, 0x1F7C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9, 0x0301, 0x03CE),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9, 0x0313, 0x1F60), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9, 0x0314, 0x1F61),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9, 0x0342, 0x1FF6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9, 0x0345, 0x1FF3),HB_CODEPOINT_ENCODE3_11_7_14 (0x03CA, 0x0300, 0x1FD2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CA, 0x0301, 0x0390),HB_CODEPOINT_ENCODE3_11_7_14 (0x03CA, 0x0342, 0x1FD7), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CB, 0x0300, 0x1FE2),HB_CODEPOINT_ENCODE3_11_7_14 (0x03CB, 0x0301, 0x03B0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CB, 0x0342, 0x1FE7),HB_CODEPOINT_ENCODE3_11_7_14 (0x03CE, 0x0345, 0x1FF4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2, 0x0301, 0x03D3),HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2, 0x0308, 0x03D4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0406, 0x0308, 0x0407),HB_CODEPOINT_ENCODE3_11_7_14 (0x0410, 0x0306, 0x04D0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0410, 0x0308, 0x04D2),HB_CODEPOINT_ENCODE3_11_7_14 (0x0413, 0x0301, 0x0403), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415, 0x0300, 0x0400),HB_CODEPOINT_ENCODE3_11_7_14 (0x0415, 0x0306, 0x04D6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415, 0x0308, 0x0401),HB_CODEPOINT_ENCODE3_11_7_14 (0x0416, 0x0306, 0x04C1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0416, 0x0308, 0x04DC),HB_CODEPOINT_ENCODE3_11_7_14 (0x0417, 0x0308, 0x04DE), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418, 0x0300, 0x040D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0418, 0x0304, 0x04E2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418, 0x0306, 0x0419),HB_CODEPOINT_ENCODE3_11_7_14 (0x0418, 0x0308, 0x04E4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x041A, 0x0301, 0x040C),HB_CODEPOINT_ENCODE3_11_7_14 (0x041E, 0x0308, 0x04E6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423, 0x0304, 0x04EE),HB_CODEPOINT_ENCODE3_11_7_14 (0x0423, 0x0306, 0x040E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423, 0x0308, 0x04F0),HB_CODEPOINT_ENCODE3_11_7_14 (0x0423, 0x030B, 0x04F2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0427, 0x0308, 0x04F4),HB_CODEPOINT_ENCODE3_11_7_14 (0x042B, 0x0308, 0x04F8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x042D, 0x0308, 0x04EC),HB_CODEPOINT_ENCODE3_11_7_14 (0x0430, 0x0306, 0x04D1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0430, 0x0308, 0x04D3),HB_CODEPOINT_ENCODE3_11_7_14 (0x0433, 0x0301, 0x0453), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435, 0x0300, 0x0450),HB_CODEPOINT_ENCODE3_11_7_14 (0x0435, 0x0306, 0x04D7), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435, 0x0308, 0x0451),HB_CODEPOINT_ENCODE3_11_7_14 (0x0436, 0x0306, 0x04C2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0436, 0x0308, 0x04DD),HB_CODEPOINT_ENCODE3_11_7_14 (0x0437, 0x0308, 0x04DF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438, 0x0300, 0x045D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0438, 0x0304, 0x04E3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438, 0x0306, 0x0439),HB_CODEPOINT_ENCODE3_11_7_14 (0x0438, 0x0308, 0x04E5), + HB_CODEPOINT_ENCODE3_11_7_14 (0x043A, 0x0301, 0x045C),HB_CODEPOINT_ENCODE3_11_7_14 (0x043E, 0x0308, 0x04E7), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443, 0x0304, 0x04EF),HB_CODEPOINT_ENCODE3_11_7_14 (0x0443, 0x0306, 0x045E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443, 0x0308, 0x04F1),HB_CODEPOINT_ENCODE3_11_7_14 (0x0443, 0x030B, 0x04F3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0447, 0x0308, 0x04F5),HB_CODEPOINT_ENCODE3_11_7_14 (0x044B, 0x0308, 0x04F9), + HB_CODEPOINT_ENCODE3_11_7_14 (0x044D, 0x0308, 0x04ED),HB_CODEPOINT_ENCODE3_11_7_14 (0x0456, 0x0308, 0x0457), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0474, 0x030F, 0x0476),HB_CODEPOINT_ENCODE3_11_7_14 (0x0475, 0x030F, 0x0477), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04D8, 0x0308, 0x04DA),HB_CODEPOINT_ENCODE3_11_7_14 (0x04D9, 0x0308, 0x04DB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04E8, 0x0308, 0x04EA),HB_CODEPOINT_ENCODE3_11_7_14 (0x04E9, 0x0308, 0x04EB), }; -static const uint64_t -_hb_ucd_dm2_u64_map[408] = +static const uint64_t _hb_ucd_dm2_u64_map[408]= { - HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05B7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05B8u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D1u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05D1u, 0x05BFu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D2u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05D3u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D4u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05D5u, 0x05B9u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D5u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05D6u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D8u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05D9u, 0x05B4u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D9u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05DAu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05DBu, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05DBu, 0x05BFu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05DCu, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05DEu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E0u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05E1u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E3u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05E4u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E4u, 0x05BFu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05E6u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E7u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05E8u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05C1u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05C2u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05EAu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05F2u, 0x05B7u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0627u, 0x0653u, 0x0622u), HB_CODEPOINT_ENCODE3 (0x0627u, 0x0654u, 0x0623u), - HB_CODEPOINT_ENCODE3 (0x0627u, 0x0655u, 0x0625u), HB_CODEPOINT_ENCODE3 (0x0648u, 0x0654u, 0x0624u), - HB_CODEPOINT_ENCODE3 (0x064Au, 0x0654u, 0x0626u), HB_CODEPOINT_ENCODE3 (0x06C1u, 0x0654u, 0x06C2u), - HB_CODEPOINT_ENCODE3 (0x06D2u, 0x0654u, 0x06D3u), HB_CODEPOINT_ENCODE3 (0x06D5u, 0x0654u, 0x06C0u), - HB_CODEPOINT_ENCODE3 (0x0915u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0916u, 0x093Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0917u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x091Cu, 0x093Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0921u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0922u, 0x093Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0928u, 0x093Cu, 0x0929u), HB_CODEPOINT_ENCODE3 (0x092Bu, 0x093Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x092Fu, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0930u, 0x093Cu, 0x0931u), - HB_CODEPOINT_ENCODE3 (0x0933u, 0x093Cu, 0x0934u), HB_CODEPOINT_ENCODE3 (0x09A1u, 0x09BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x09A2u, 0x09BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x09AFu, 0x09BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x09C7u, 0x09BEu, 0x09CBu), HB_CODEPOINT_ENCODE3 (0x09C7u, 0x09D7u, 0x09CCu), - HB_CODEPOINT_ENCODE3 (0x0A16u, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A17u, 0x0A3Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0A1Cu, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A2Bu, 0x0A3Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0A32u, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A38u, 0x0A3Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0B21u, 0x0B3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0B22u, 0x0B3Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B3Eu, 0x0B4Bu), HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B56u, 0x0B48u), - HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B57u, 0x0B4Cu), HB_CODEPOINT_ENCODE3 (0x0B92u, 0x0BD7u, 0x0B94u), - HB_CODEPOINT_ENCODE3 (0x0BC6u, 0x0BBEu, 0x0BCAu), HB_CODEPOINT_ENCODE3 (0x0BC6u, 0x0BD7u, 0x0BCCu), - HB_CODEPOINT_ENCODE3 (0x0BC7u, 0x0BBEu, 0x0BCBu), HB_CODEPOINT_ENCODE3 (0x0C46u, 0x0C56u, 0x0C48u), - HB_CODEPOINT_ENCODE3 (0x0CBFu, 0x0CD5u, 0x0CC0u), HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CC2u, 0x0CCAu), - HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CD5u, 0x0CC7u), HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CD6u, 0x0CC8u), - HB_CODEPOINT_ENCODE3 (0x0CCAu, 0x0CD5u, 0x0CCBu), HB_CODEPOINT_ENCODE3 (0x0D46u, 0x0D3Eu, 0x0D4Au), - HB_CODEPOINT_ENCODE3 (0x0D46u, 0x0D57u, 0x0D4Cu), HB_CODEPOINT_ENCODE3 (0x0D47u, 0x0D3Eu, 0x0D4Bu), - HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DCAu, 0x0DDAu), HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DCFu, 0x0DDCu), - HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DDFu, 0x0DDEu), HB_CODEPOINT_ENCODE3 (0x0DDCu, 0x0DCAu, 0x0DDDu), - HB_CODEPOINT_ENCODE3 (0x0F40u, 0x0FB5u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F42u, 0x0FB7u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0F4Cu, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F51u, 0x0FB7u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0F56u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F5Bu, 0x0FB7u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F72u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F74u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F80u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F90u, 0x0FB5u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0F92u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F9Cu, 0x0FB7u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0FA1u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0FA6u, 0x0FB7u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0FABu, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0FB2u, 0x0F80u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0FB3u, 0x0F80u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1025u, 0x102Eu, 0x1026u), - HB_CODEPOINT_ENCODE3 (0x1B05u, 0x1B35u, 0x1B06u), HB_CODEPOINT_ENCODE3 (0x1B07u, 0x1B35u, 0x1B08u), - HB_CODEPOINT_ENCODE3 (0x1B09u, 0x1B35u, 0x1B0Au), HB_CODEPOINT_ENCODE3 (0x1B0Bu, 0x1B35u, 0x1B0Cu), - HB_CODEPOINT_ENCODE3 (0x1B0Du, 0x1B35u, 0x1B0Eu), HB_CODEPOINT_ENCODE3 (0x1B11u, 0x1B35u, 0x1B12u), - HB_CODEPOINT_ENCODE3 (0x1B3Au, 0x1B35u, 0x1B3Bu), HB_CODEPOINT_ENCODE3 (0x1B3Cu, 0x1B35u, 0x1B3Du), - HB_CODEPOINT_ENCODE3 (0x1B3Eu, 0x1B35u, 0x1B40u), HB_CODEPOINT_ENCODE3 (0x1B3Fu, 0x1B35u, 0x1B41u), - HB_CODEPOINT_ENCODE3 (0x1B42u, 0x1B35u, 0x1B43u), HB_CODEPOINT_ENCODE3 (0x1E36u, 0x0304u, 0x1E38u), - HB_CODEPOINT_ENCODE3 (0x1E37u, 0x0304u, 0x1E39u), HB_CODEPOINT_ENCODE3 (0x1E5Au, 0x0304u, 0x1E5Cu), - HB_CODEPOINT_ENCODE3 (0x1E5Bu, 0x0304u, 0x1E5Du), HB_CODEPOINT_ENCODE3 (0x1E62u, 0x0307u, 0x1E68u), - HB_CODEPOINT_ENCODE3 (0x1E63u, 0x0307u, 0x1E69u), HB_CODEPOINT_ENCODE3 (0x1EA0u, 0x0302u, 0x1EACu), - HB_CODEPOINT_ENCODE3 (0x1EA0u, 0x0306u, 0x1EB6u), HB_CODEPOINT_ENCODE3 (0x1EA1u, 0x0302u, 0x1EADu), - HB_CODEPOINT_ENCODE3 (0x1EA1u, 0x0306u, 0x1EB7u), HB_CODEPOINT_ENCODE3 (0x1EB8u, 0x0302u, 0x1EC6u), - HB_CODEPOINT_ENCODE3 (0x1EB9u, 0x0302u, 0x1EC7u), HB_CODEPOINT_ENCODE3 (0x1ECCu, 0x0302u, 0x1ED8u), - HB_CODEPOINT_ENCODE3 (0x1ECDu, 0x0302u, 0x1ED9u), HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0300u, 0x1F02u), - HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0301u, 0x1F04u), HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0342u, 0x1F06u), - HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0345u, 0x1F80u), HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0300u, 0x1F03u), - HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0301u, 0x1F05u), HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0342u, 0x1F07u), - HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0345u, 0x1F81u), HB_CODEPOINT_ENCODE3 (0x1F02u, 0x0345u, 0x1F82u), - HB_CODEPOINT_ENCODE3 (0x1F03u, 0x0345u, 0x1F83u), HB_CODEPOINT_ENCODE3 (0x1F04u, 0x0345u, 0x1F84u), - HB_CODEPOINT_ENCODE3 (0x1F05u, 0x0345u, 0x1F85u), HB_CODEPOINT_ENCODE3 (0x1F06u, 0x0345u, 0x1F86u), - HB_CODEPOINT_ENCODE3 (0x1F07u, 0x0345u, 0x1F87u), HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0300u, 0x1F0Au), - HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0301u, 0x1F0Cu), HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0342u, 0x1F0Eu), - HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0345u, 0x1F88u), HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0300u, 0x1F0Bu), - HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0301u, 0x1F0Du), HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0342u, 0x1F0Fu), - HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0345u, 0x1F89u), HB_CODEPOINT_ENCODE3 (0x1F0Au, 0x0345u, 0x1F8Au), - HB_CODEPOINT_ENCODE3 (0x1F0Bu, 0x0345u, 0x1F8Bu), HB_CODEPOINT_ENCODE3 (0x1F0Cu, 0x0345u, 0x1F8Cu), - HB_CODEPOINT_ENCODE3 (0x1F0Du, 0x0345u, 0x1F8Du), HB_CODEPOINT_ENCODE3 (0x1F0Eu, 0x0345u, 0x1F8Eu), - HB_CODEPOINT_ENCODE3 (0x1F0Fu, 0x0345u, 0x1F8Fu), HB_CODEPOINT_ENCODE3 (0x1F10u, 0x0300u, 0x1F12u), - HB_CODEPOINT_ENCODE3 (0x1F10u, 0x0301u, 0x1F14u), HB_CODEPOINT_ENCODE3 (0x1F11u, 0x0300u, 0x1F13u), - HB_CODEPOINT_ENCODE3 (0x1F11u, 0x0301u, 0x1F15u), HB_CODEPOINT_ENCODE3 (0x1F18u, 0x0300u, 0x1F1Au), - HB_CODEPOINT_ENCODE3 (0x1F18u, 0x0301u, 0x1F1Cu), HB_CODEPOINT_ENCODE3 (0x1F19u, 0x0300u, 0x1F1Bu), - HB_CODEPOINT_ENCODE3 (0x1F19u, 0x0301u, 0x1F1Du), HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0300u, 0x1F22u), - HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0301u, 0x1F24u), HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0342u, 0x1F26u), - HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0345u, 0x1F90u), HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0300u, 0x1F23u), - HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0301u, 0x1F25u), HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0342u, 0x1F27u), - HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0345u, 0x1F91u), HB_CODEPOINT_ENCODE3 (0x1F22u, 0x0345u, 0x1F92u), - HB_CODEPOINT_ENCODE3 (0x1F23u, 0x0345u, 0x1F93u), HB_CODEPOINT_ENCODE3 (0x1F24u, 0x0345u, 0x1F94u), - HB_CODEPOINT_ENCODE3 (0x1F25u, 0x0345u, 0x1F95u), HB_CODEPOINT_ENCODE3 (0x1F26u, 0x0345u, 0x1F96u), - HB_CODEPOINT_ENCODE3 (0x1F27u, 0x0345u, 0x1F97u), HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0300u, 0x1F2Au), - HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0301u, 0x1F2Cu), HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0342u, 0x1F2Eu), - HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0345u, 0x1F98u), HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0300u, 0x1F2Bu), - HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0301u, 0x1F2Du), HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0342u, 0x1F2Fu), - HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0345u, 0x1F99u), HB_CODEPOINT_ENCODE3 (0x1F2Au, 0x0345u, 0x1F9Au), - HB_CODEPOINT_ENCODE3 (0x1F2Bu, 0x0345u, 0x1F9Bu), HB_CODEPOINT_ENCODE3 (0x1F2Cu, 0x0345u, 0x1F9Cu), - HB_CODEPOINT_ENCODE3 (0x1F2Du, 0x0345u, 0x1F9Du), HB_CODEPOINT_ENCODE3 (0x1F2Eu, 0x0345u, 0x1F9Eu), - HB_CODEPOINT_ENCODE3 (0x1F2Fu, 0x0345u, 0x1F9Fu), HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0300u, 0x1F32u), - HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0301u, 0x1F34u), HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0342u, 0x1F36u), - HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0300u, 0x1F33u), HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0301u, 0x1F35u), - HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0342u, 0x1F37u), HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0300u, 0x1F3Au), - HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0301u, 0x1F3Cu), HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0342u, 0x1F3Eu), - HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0300u, 0x1F3Bu), HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0301u, 0x1F3Du), - HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0342u, 0x1F3Fu), HB_CODEPOINT_ENCODE3 (0x1F40u, 0x0300u, 0x1F42u), - HB_CODEPOINT_ENCODE3 (0x1F40u, 0x0301u, 0x1F44u), HB_CODEPOINT_ENCODE3 (0x1F41u, 0x0300u, 0x1F43u), - HB_CODEPOINT_ENCODE3 (0x1F41u, 0x0301u, 0x1F45u), HB_CODEPOINT_ENCODE3 (0x1F48u, 0x0300u, 0x1F4Au), - HB_CODEPOINT_ENCODE3 (0x1F48u, 0x0301u, 0x1F4Cu), HB_CODEPOINT_ENCODE3 (0x1F49u, 0x0300u, 0x1F4Bu), - HB_CODEPOINT_ENCODE3 (0x1F49u, 0x0301u, 0x1F4Du), HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0300u, 0x1F52u), - HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0301u, 0x1F54u), HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0342u, 0x1F56u), - HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0300u, 0x1F53u), HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0301u, 0x1F55u), - HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0342u, 0x1F57u), HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0300u, 0x1F5Bu), - HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0301u, 0x1F5Du), HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0342u, 0x1F5Fu), - HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0300u, 0x1F62u), HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0301u, 0x1F64u), - HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0342u, 0x1F66u), HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0345u, 0x1FA0u), - HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0300u, 0x1F63u), HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0301u, 0x1F65u), - HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0342u, 0x1F67u), HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0345u, 0x1FA1u), - HB_CODEPOINT_ENCODE3 (0x1F62u, 0x0345u, 0x1FA2u), HB_CODEPOINT_ENCODE3 (0x1F63u, 0x0345u, 0x1FA3u), - HB_CODEPOINT_ENCODE3 (0x1F64u, 0x0345u, 0x1FA4u), HB_CODEPOINT_ENCODE3 (0x1F65u, 0x0345u, 0x1FA5u), - HB_CODEPOINT_ENCODE3 (0x1F66u, 0x0345u, 0x1FA6u), HB_CODEPOINT_ENCODE3 (0x1F67u, 0x0345u, 0x1FA7u), - HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0300u, 0x1F6Au), HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0301u, 0x1F6Cu), - HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0342u, 0x1F6Eu), HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0345u, 0x1FA8u), - HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0300u, 0x1F6Bu), HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0301u, 0x1F6Du), - HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0342u, 0x1F6Fu), HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0345u, 0x1FA9u), - HB_CODEPOINT_ENCODE3 (0x1F6Au, 0x0345u, 0x1FAAu), HB_CODEPOINT_ENCODE3 (0x1F6Bu, 0x0345u, 0x1FABu), - HB_CODEPOINT_ENCODE3 (0x1F6Cu, 0x0345u, 0x1FACu), HB_CODEPOINT_ENCODE3 (0x1F6Du, 0x0345u, 0x1FADu), - HB_CODEPOINT_ENCODE3 (0x1F6Eu, 0x0345u, 0x1FAEu), HB_CODEPOINT_ENCODE3 (0x1F6Fu, 0x0345u, 0x1FAFu), - HB_CODEPOINT_ENCODE3 (0x1F70u, 0x0345u, 0x1FB2u), HB_CODEPOINT_ENCODE3 (0x1F74u, 0x0345u, 0x1FC2u), - HB_CODEPOINT_ENCODE3 (0x1F7Cu, 0x0345u, 0x1FF2u), HB_CODEPOINT_ENCODE3 (0x1FB6u, 0x0345u, 0x1FB7u), - HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0300u, 0x1FCDu), HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0301u, 0x1FCEu), - HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0342u, 0x1FCFu), HB_CODEPOINT_ENCODE3 (0x1FC6u, 0x0345u, 0x1FC7u), - HB_CODEPOINT_ENCODE3 (0x1FF6u, 0x0345u, 0x1FF7u), HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0300u, 0x1FDDu), - HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0301u, 0x1FDEu), HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0342u, 0x1FDFu), - HB_CODEPOINT_ENCODE3 (0x2190u, 0x0338u, 0x219Au), HB_CODEPOINT_ENCODE3 (0x2192u, 0x0338u, 0x219Bu), - HB_CODEPOINT_ENCODE3 (0x2194u, 0x0338u, 0x21AEu), HB_CODEPOINT_ENCODE3 (0x21D0u, 0x0338u, 0x21CDu), - HB_CODEPOINT_ENCODE3 (0x21D2u, 0x0338u, 0x21CFu), HB_CODEPOINT_ENCODE3 (0x21D4u, 0x0338u, 0x21CEu), - HB_CODEPOINT_ENCODE3 (0x2203u, 0x0338u, 0x2204u), HB_CODEPOINT_ENCODE3 (0x2208u, 0x0338u, 0x2209u), - HB_CODEPOINT_ENCODE3 (0x220Bu, 0x0338u, 0x220Cu), HB_CODEPOINT_ENCODE3 (0x2223u, 0x0338u, 0x2224u), - HB_CODEPOINT_ENCODE3 (0x2225u, 0x0338u, 0x2226u), HB_CODEPOINT_ENCODE3 (0x223Cu, 0x0338u, 0x2241u), - HB_CODEPOINT_ENCODE3 (0x2243u, 0x0338u, 0x2244u), HB_CODEPOINT_ENCODE3 (0x2245u, 0x0338u, 0x2247u), - HB_CODEPOINT_ENCODE3 (0x2248u, 0x0338u, 0x2249u), HB_CODEPOINT_ENCODE3 (0x224Du, 0x0338u, 0x226Du), - HB_CODEPOINT_ENCODE3 (0x2261u, 0x0338u, 0x2262u), HB_CODEPOINT_ENCODE3 (0x2264u, 0x0338u, 0x2270u), - HB_CODEPOINT_ENCODE3 (0x2265u, 0x0338u, 0x2271u), HB_CODEPOINT_ENCODE3 (0x2272u, 0x0338u, 0x2274u), - HB_CODEPOINT_ENCODE3 (0x2273u, 0x0338u, 0x2275u), HB_CODEPOINT_ENCODE3 (0x2276u, 0x0338u, 0x2278u), - HB_CODEPOINT_ENCODE3 (0x2277u, 0x0338u, 0x2279u), HB_CODEPOINT_ENCODE3 (0x227Au, 0x0338u, 0x2280u), - HB_CODEPOINT_ENCODE3 (0x227Bu, 0x0338u, 0x2281u), HB_CODEPOINT_ENCODE3 (0x227Cu, 0x0338u, 0x22E0u), - HB_CODEPOINT_ENCODE3 (0x227Du, 0x0338u, 0x22E1u), HB_CODEPOINT_ENCODE3 (0x2282u, 0x0338u, 0x2284u), - HB_CODEPOINT_ENCODE3 (0x2283u, 0x0338u, 0x2285u), HB_CODEPOINT_ENCODE3 (0x2286u, 0x0338u, 0x2288u), - HB_CODEPOINT_ENCODE3 (0x2287u, 0x0338u, 0x2289u), HB_CODEPOINT_ENCODE3 (0x2291u, 0x0338u, 0x22E2u), - HB_CODEPOINT_ENCODE3 (0x2292u, 0x0338u, 0x22E3u), HB_CODEPOINT_ENCODE3 (0x22A2u, 0x0338u, 0x22ACu), - HB_CODEPOINT_ENCODE3 (0x22A8u, 0x0338u, 0x22ADu), HB_CODEPOINT_ENCODE3 (0x22A9u, 0x0338u, 0x22AEu), - HB_CODEPOINT_ENCODE3 (0x22ABu, 0x0338u, 0x22AFu), HB_CODEPOINT_ENCODE3 (0x22B2u, 0x0338u, 0x22EAu), - HB_CODEPOINT_ENCODE3 (0x22B3u, 0x0338u, 0x22EBu), HB_CODEPOINT_ENCODE3 (0x22B4u, 0x0338u, 0x22ECu), - HB_CODEPOINT_ENCODE3 (0x22B5u, 0x0338u, 0x22EDu), HB_CODEPOINT_ENCODE3 (0x2ADDu, 0x0338u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x3046u, 0x3099u, 0x3094u), HB_CODEPOINT_ENCODE3 (0x304Bu, 0x3099u, 0x304Cu), - HB_CODEPOINT_ENCODE3 (0x304Du, 0x3099u, 0x304Eu), HB_CODEPOINT_ENCODE3 (0x304Fu, 0x3099u, 0x3050u), - HB_CODEPOINT_ENCODE3 (0x3051u, 0x3099u, 0x3052u), HB_CODEPOINT_ENCODE3 (0x3053u, 0x3099u, 0x3054u), - HB_CODEPOINT_ENCODE3 (0x3055u, 0x3099u, 0x3056u), HB_CODEPOINT_ENCODE3 (0x3057u, 0x3099u, 0x3058u), - HB_CODEPOINT_ENCODE3 (0x3059u, 0x3099u, 0x305Au), HB_CODEPOINT_ENCODE3 (0x305Bu, 0x3099u, 0x305Cu), - HB_CODEPOINT_ENCODE3 (0x305Du, 0x3099u, 0x305Eu), HB_CODEPOINT_ENCODE3 (0x305Fu, 0x3099u, 0x3060u), - HB_CODEPOINT_ENCODE3 (0x3061u, 0x3099u, 0x3062u), HB_CODEPOINT_ENCODE3 (0x3064u, 0x3099u, 0x3065u), - HB_CODEPOINT_ENCODE3 (0x3066u, 0x3099u, 0x3067u), HB_CODEPOINT_ENCODE3 (0x3068u, 0x3099u, 0x3069u), - HB_CODEPOINT_ENCODE3 (0x306Fu, 0x3099u, 0x3070u), HB_CODEPOINT_ENCODE3 (0x306Fu, 0x309Au, 0x3071u), - HB_CODEPOINT_ENCODE3 (0x3072u, 0x3099u, 0x3073u), HB_CODEPOINT_ENCODE3 (0x3072u, 0x309Au, 0x3074u), - HB_CODEPOINT_ENCODE3 (0x3075u, 0x3099u, 0x3076u), HB_CODEPOINT_ENCODE3 (0x3075u, 0x309Au, 0x3077u), - HB_CODEPOINT_ENCODE3 (0x3078u, 0x3099u, 0x3079u), HB_CODEPOINT_ENCODE3 (0x3078u, 0x309Au, 0x307Au), - HB_CODEPOINT_ENCODE3 (0x307Bu, 0x3099u, 0x307Cu), HB_CODEPOINT_ENCODE3 (0x307Bu, 0x309Au, 0x307Du), - HB_CODEPOINT_ENCODE3 (0x309Du, 0x3099u, 0x309Eu), HB_CODEPOINT_ENCODE3 (0x30A6u, 0x3099u, 0x30F4u), - HB_CODEPOINT_ENCODE3 (0x30ABu, 0x3099u, 0x30ACu), HB_CODEPOINT_ENCODE3 (0x30ADu, 0x3099u, 0x30AEu), - HB_CODEPOINT_ENCODE3 (0x30AFu, 0x3099u, 0x30B0u), HB_CODEPOINT_ENCODE3 (0x30B1u, 0x3099u, 0x30B2u), - HB_CODEPOINT_ENCODE3 (0x30B3u, 0x3099u, 0x30B4u), HB_CODEPOINT_ENCODE3 (0x30B5u, 0x3099u, 0x30B6u), - HB_CODEPOINT_ENCODE3 (0x30B7u, 0x3099u, 0x30B8u), HB_CODEPOINT_ENCODE3 (0x30B9u, 0x3099u, 0x30BAu), - HB_CODEPOINT_ENCODE3 (0x30BBu, 0x3099u, 0x30BCu), HB_CODEPOINT_ENCODE3 (0x30BDu, 0x3099u, 0x30BEu), - HB_CODEPOINT_ENCODE3 (0x30BFu, 0x3099u, 0x30C0u), HB_CODEPOINT_ENCODE3 (0x30C1u, 0x3099u, 0x30C2u), - HB_CODEPOINT_ENCODE3 (0x30C4u, 0x3099u, 0x30C5u), HB_CODEPOINT_ENCODE3 (0x30C6u, 0x3099u, 0x30C7u), - HB_CODEPOINT_ENCODE3 (0x30C8u, 0x3099u, 0x30C9u), HB_CODEPOINT_ENCODE3 (0x30CFu, 0x3099u, 0x30D0u), - HB_CODEPOINT_ENCODE3 (0x30CFu, 0x309Au, 0x30D1u), HB_CODEPOINT_ENCODE3 (0x30D2u, 0x3099u, 0x30D3u), - HB_CODEPOINT_ENCODE3 (0x30D2u, 0x309Au, 0x30D4u), HB_CODEPOINT_ENCODE3 (0x30D5u, 0x3099u, 0x30D6u), - HB_CODEPOINT_ENCODE3 (0x30D5u, 0x309Au, 0x30D7u), HB_CODEPOINT_ENCODE3 (0x30D8u, 0x3099u, 0x30D9u), - HB_CODEPOINT_ENCODE3 (0x30D8u, 0x309Au, 0x30DAu), HB_CODEPOINT_ENCODE3 (0x30DBu, 0x3099u, 0x30DCu), - HB_CODEPOINT_ENCODE3 (0x30DBu, 0x309Au, 0x30DDu), HB_CODEPOINT_ENCODE3 (0x30EFu, 0x3099u, 0x30F7u), - HB_CODEPOINT_ENCODE3 (0x30F0u, 0x3099u, 0x30F8u), HB_CODEPOINT_ENCODE3 (0x30F1u, 0x3099u, 0x30F9u), - HB_CODEPOINT_ENCODE3 (0x30F2u, 0x3099u, 0x30FAu), HB_CODEPOINT_ENCODE3 (0x30FDu, 0x3099u, 0x30FEu), - HB_CODEPOINT_ENCODE3 (0xFB49u, 0x05C1u, 0x0000u), HB_CODEPOINT_ENCODE3 (0xFB49u, 0x05C2u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x105D2u, 0x0307u, 0x105C9u), HB_CODEPOINT_ENCODE3 (0x105DAu, 0x0307u, 0x105E4u), - HB_CODEPOINT_ENCODE3 (0x11099u, 0x110BAu, 0x1109Au),HB_CODEPOINT_ENCODE3 (0x1109Bu, 0x110BAu, 0x1109Cu), - HB_CODEPOINT_ENCODE3 (0x110A5u, 0x110BAu, 0x110ABu),HB_CODEPOINT_ENCODE3 (0x11131u, 0x11127u, 0x1112Eu), - HB_CODEPOINT_ENCODE3 (0x11132u, 0x11127u, 0x1112Fu),HB_CODEPOINT_ENCODE3 (0x11347u, 0x1133Eu, 0x1134Bu), - HB_CODEPOINT_ENCODE3 (0x11347u, 0x11357u, 0x1134Cu),HB_CODEPOINT_ENCODE3 (0x11382u, 0x113C9u, 0x11383u), - HB_CODEPOINT_ENCODE3 (0x11384u, 0x113BBu, 0x11385u),HB_CODEPOINT_ENCODE3 (0x1138Bu, 0x113C2u, 0x1138Eu), - HB_CODEPOINT_ENCODE3 (0x11390u, 0x113C9u, 0x11391u),HB_CODEPOINT_ENCODE3 (0x113C2u, 0x113B8u, 0x113C7u), - HB_CODEPOINT_ENCODE3 (0x113C2u, 0x113C2u, 0x113C5u),HB_CODEPOINT_ENCODE3 (0x113C2u, 0x113C9u, 0x113C8u), - HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114B0u, 0x114BCu),HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BAu, 0x114BBu), - HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BDu, 0x114BEu),HB_CODEPOINT_ENCODE3 (0x115B8u, 0x115AFu, 0x115BAu), - HB_CODEPOINT_ENCODE3 (0x115B9u, 0x115AFu, 0x115BBu),HB_CODEPOINT_ENCODE3 (0x11935u, 0x11930u, 0x11938u), - HB_CODEPOINT_ENCODE3 (0x1611Eu, 0x1611Eu, 0x16121u),HB_CODEPOINT_ENCODE3 (0x1611Eu, 0x1611Fu, 0x16123u), - HB_CODEPOINT_ENCODE3 (0x1611Eu, 0x16120u, 0x16125u),HB_CODEPOINT_ENCODE3 (0x1611Eu, 0x16129u, 0x16122u), - HB_CODEPOINT_ENCODE3 (0x16121u, 0x1611Fu, 0x16126u),HB_CODEPOINT_ENCODE3 (0x16121u, 0x16120u, 0x16128u), - HB_CODEPOINT_ENCODE3 (0x16122u, 0x1611Fu, 0x16127u),HB_CODEPOINT_ENCODE3 (0x16129u, 0x1611Fu, 0x16124u), - HB_CODEPOINT_ENCODE3 (0x16D63u, 0x16D67u, 0x16D69u),HB_CODEPOINT_ENCODE3 (0x16D67u, 0x16D67u, 0x16D68u), - HB_CODEPOINT_ENCODE3 (0x16D69u, 0x16D67u, 0x16D6Au), HB_CODEPOINT_ENCODE3 (0x1D157u, 0x1D165u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x1D158u, 0x1D165u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D16Eu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D16Fu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D170u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D171u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D172u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x1D1B9u, 0x1D165u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BAu, 0x1D165u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x1D1BBu, 0x1D16Eu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BBu, 0x1D16Fu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x1D1BCu, 0x1D16Eu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BCu, 0x1D16Fu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D0, 0x05B7, 0x0000), HB_CODEPOINT_ENCODE3 (0x05D0, 0x05B8, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05D0, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05D1, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05D1, 0x05BF, 0x0000), HB_CODEPOINT_ENCODE3 (0x05D2, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05D3, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05D4, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05D5, 0x05B9, 0x0000), HB_CODEPOINT_ENCODE3 (0x05D5, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05D6, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05D8, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05D9, 0x05B4, 0x0000), HB_CODEPOINT_ENCODE3 (0x05D9, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05DA, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05DB, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05DB, 0x05BF, 0x0000), HB_CODEPOINT_ENCODE3 (0x05DC, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05DE, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05E0, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05E1, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05E3, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05E4, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05E4, 0x05BF, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05E6, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05E7, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05E8, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05E9, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05E9, 0x05C1, 0x0000), HB_CODEPOINT_ENCODE3 (0x05E9, 0x05C2, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05EA, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05F2, 0x05B7, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0627, 0x0653, 0x0622), HB_CODEPOINT_ENCODE3 (0x0627, 0x0654, 0x0623), + HB_CODEPOINT_ENCODE3 (0x0627, 0x0655, 0x0625), HB_CODEPOINT_ENCODE3 (0x0648, 0x0654, 0x0624), + HB_CODEPOINT_ENCODE3 (0x064A, 0x0654, 0x0626), HB_CODEPOINT_ENCODE3 (0x06C1, 0x0654, 0x06C2), + HB_CODEPOINT_ENCODE3 (0x06D2, 0x0654, 0x06D3), HB_CODEPOINT_ENCODE3 (0x06D5, 0x0654, 0x06C0), + HB_CODEPOINT_ENCODE3 (0x0915, 0x093C, 0x0000), HB_CODEPOINT_ENCODE3 (0x0916, 0x093C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0917, 0x093C, 0x0000), HB_CODEPOINT_ENCODE3 (0x091C, 0x093C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0921, 0x093C, 0x0000), HB_CODEPOINT_ENCODE3 (0x0922, 0x093C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0928, 0x093C, 0x0929), HB_CODEPOINT_ENCODE3 (0x092B, 0x093C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x092F, 0x093C, 0x0000), HB_CODEPOINT_ENCODE3 (0x0930, 0x093C, 0x0931), + HB_CODEPOINT_ENCODE3 (0x0933, 0x093C, 0x0934), HB_CODEPOINT_ENCODE3 (0x09A1, 0x09BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x09A2, 0x09BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x09AF, 0x09BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x09C7, 0x09BE, 0x09CB), HB_CODEPOINT_ENCODE3 (0x09C7, 0x09D7, 0x09CC), + HB_CODEPOINT_ENCODE3 (0x0A16, 0x0A3C, 0x0000), HB_CODEPOINT_ENCODE3 (0x0A17, 0x0A3C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0A1C, 0x0A3C, 0x0000), HB_CODEPOINT_ENCODE3 (0x0A2B, 0x0A3C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0A32, 0x0A3C, 0x0000), HB_CODEPOINT_ENCODE3 (0x0A38, 0x0A3C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0B21, 0x0B3C, 0x0000), HB_CODEPOINT_ENCODE3 (0x0B22, 0x0B3C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0B47, 0x0B3E, 0x0B4B), HB_CODEPOINT_ENCODE3 (0x0B47, 0x0B56, 0x0B48), + HB_CODEPOINT_ENCODE3 (0x0B47, 0x0B57, 0x0B4C), HB_CODEPOINT_ENCODE3 (0x0B92, 0x0BD7, 0x0B94), + HB_CODEPOINT_ENCODE3 (0x0BC6, 0x0BBE, 0x0BCA), HB_CODEPOINT_ENCODE3 (0x0BC6, 0x0BD7, 0x0BCC), + HB_CODEPOINT_ENCODE3 (0x0BC7, 0x0BBE, 0x0BCB), HB_CODEPOINT_ENCODE3 (0x0C46, 0x0C56, 0x0C48), + HB_CODEPOINT_ENCODE3 (0x0CBF, 0x0CD5, 0x0CC0), HB_CODEPOINT_ENCODE3 (0x0CC6, 0x0CC2, 0x0CCA), + HB_CODEPOINT_ENCODE3 (0x0CC6, 0x0CD5, 0x0CC7), HB_CODEPOINT_ENCODE3 (0x0CC6, 0x0CD6, 0x0CC8), + HB_CODEPOINT_ENCODE3 (0x0CCA, 0x0CD5, 0x0CCB), HB_CODEPOINT_ENCODE3 (0x0D46, 0x0D3E, 0x0D4A), + HB_CODEPOINT_ENCODE3 (0x0D46, 0x0D57, 0x0D4C), HB_CODEPOINT_ENCODE3 (0x0D47, 0x0D3E, 0x0D4B), + HB_CODEPOINT_ENCODE3 (0x0DD9, 0x0DCA, 0x0DDA), HB_CODEPOINT_ENCODE3 (0x0DD9, 0x0DCF, 0x0DDC), + HB_CODEPOINT_ENCODE3 (0x0DD9, 0x0DDF, 0x0DDE), HB_CODEPOINT_ENCODE3 (0x0DDC, 0x0DCA, 0x0DDD), + HB_CODEPOINT_ENCODE3 (0x0F40, 0x0FB5, 0x0000), HB_CODEPOINT_ENCODE3 (0x0F42, 0x0FB7, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0F4C, 0x0FB7, 0x0000), HB_CODEPOINT_ENCODE3 (0x0F51, 0x0FB7, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0F56, 0x0FB7, 0x0000), HB_CODEPOINT_ENCODE3 (0x0F5B, 0x0FB7, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0F71, 0x0F72, 0x0000), HB_CODEPOINT_ENCODE3 (0x0F71, 0x0F74, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0F71, 0x0F80, 0x0000), HB_CODEPOINT_ENCODE3 (0x0F90, 0x0FB5, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0F92, 0x0FB7, 0x0000), HB_CODEPOINT_ENCODE3 (0x0F9C, 0x0FB7, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0FA1, 0x0FB7, 0x0000), HB_CODEPOINT_ENCODE3 (0x0FA6, 0x0FB7, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0FAB, 0x0FB7, 0x0000), HB_CODEPOINT_ENCODE3 (0x0FB2, 0x0F80, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0FB3, 0x0F80, 0x0000), HB_CODEPOINT_ENCODE3 (0x1025, 0x102E, 0x1026), + HB_CODEPOINT_ENCODE3 (0x1B05, 0x1B35, 0x1B06), HB_CODEPOINT_ENCODE3 (0x1B07, 0x1B35, 0x1B08), + HB_CODEPOINT_ENCODE3 (0x1B09, 0x1B35, 0x1B0A), HB_CODEPOINT_ENCODE3 (0x1B0B, 0x1B35, 0x1B0C), + HB_CODEPOINT_ENCODE3 (0x1B0D, 0x1B35, 0x1B0E), HB_CODEPOINT_ENCODE3 (0x1B11, 0x1B35, 0x1B12), + HB_CODEPOINT_ENCODE3 (0x1B3A, 0x1B35, 0x1B3B), HB_CODEPOINT_ENCODE3 (0x1B3C, 0x1B35, 0x1B3D), + HB_CODEPOINT_ENCODE3 (0x1B3E, 0x1B35, 0x1B40), HB_CODEPOINT_ENCODE3 (0x1B3F, 0x1B35, 0x1B41), + HB_CODEPOINT_ENCODE3 (0x1B42, 0x1B35, 0x1B43), HB_CODEPOINT_ENCODE3 (0x1E36, 0x0304, 0x1E38), + HB_CODEPOINT_ENCODE3 (0x1E37, 0x0304, 0x1E39), HB_CODEPOINT_ENCODE3 (0x1E5A, 0x0304, 0x1E5C), + HB_CODEPOINT_ENCODE3 (0x1E5B, 0x0304, 0x1E5D), HB_CODEPOINT_ENCODE3 (0x1E62, 0x0307, 0x1E68), + HB_CODEPOINT_ENCODE3 (0x1E63, 0x0307, 0x1E69), HB_CODEPOINT_ENCODE3 (0x1EA0, 0x0302, 0x1EAC), + HB_CODEPOINT_ENCODE3 (0x1EA0, 0x0306, 0x1EB6), HB_CODEPOINT_ENCODE3 (0x1EA1, 0x0302, 0x1EAD), + HB_CODEPOINT_ENCODE3 (0x1EA1, 0x0306, 0x1EB7), HB_CODEPOINT_ENCODE3 (0x1EB8, 0x0302, 0x1EC6), + HB_CODEPOINT_ENCODE3 (0x1EB9, 0x0302, 0x1EC7), HB_CODEPOINT_ENCODE3 (0x1ECC, 0x0302, 0x1ED8), + HB_CODEPOINT_ENCODE3 (0x1ECD, 0x0302, 0x1ED9), HB_CODEPOINT_ENCODE3 (0x1F00, 0x0300, 0x1F02), + HB_CODEPOINT_ENCODE3 (0x1F00, 0x0301, 0x1F04), HB_CODEPOINT_ENCODE3 (0x1F00, 0x0342, 0x1F06), + HB_CODEPOINT_ENCODE3 (0x1F00, 0x0345, 0x1F80), HB_CODEPOINT_ENCODE3 (0x1F01, 0x0300, 0x1F03), + HB_CODEPOINT_ENCODE3 (0x1F01, 0x0301, 0x1F05), HB_CODEPOINT_ENCODE3 (0x1F01, 0x0342, 0x1F07), + HB_CODEPOINT_ENCODE3 (0x1F01, 0x0345, 0x1F81), HB_CODEPOINT_ENCODE3 (0x1F02, 0x0345, 0x1F82), + HB_CODEPOINT_ENCODE3 (0x1F03, 0x0345, 0x1F83), HB_CODEPOINT_ENCODE3 (0x1F04, 0x0345, 0x1F84), + HB_CODEPOINT_ENCODE3 (0x1F05, 0x0345, 0x1F85), HB_CODEPOINT_ENCODE3 (0x1F06, 0x0345, 0x1F86), + HB_CODEPOINT_ENCODE3 (0x1F07, 0x0345, 0x1F87), HB_CODEPOINT_ENCODE3 (0x1F08, 0x0300, 0x1F0A), + HB_CODEPOINT_ENCODE3 (0x1F08, 0x0301, 0x1F0C), HB_CODEPOINT_ENCODE3 (0x1F08, 0x0342, 0x1F0E), + HB_CODEPOINT_ENCODE3 (0x1F08, 0x0345, 0x1F88), HB_CODEPOINT_ENCODE3 (0x1F09, 0x0300, 0x1F0B), + HB_CODEPOINT_ENCODE3 (0x1F09, 0x0301, 0x1F0D), HB_CODEPOINT_ENCODE3 (0x1F09, 0x0342, 0x1F0F), + HB_CODEPOINT_ENCODE3 (0x1F09, 0x0345, 0x1F89), HB_CODEPOINT_ENCODE3 (0x1F0A, 0x0345, 0x1F8A), + HB_CODEPOINT_ENCODE3 (0x1F0B, 0x0345, 0x1F8B), HB_CODEPOINT_ENCODE3 (0x1F0C, 0x0345, 0x1F8C), + HB_CODEPOINT_ENCODE3 (0x1F0D, 0x0345, 0x1F8D), HB_CODEPOINT_ENCODE3 (0x1F0E, 0x0345, 0x1F8E), + HB_CODEPOINT_ENCODE3 (0x1F0F, 0x0345, 0x1F8F), HB_CODEPOINT_ENCODE3 (0x1F10, 0x0300, 0x1F12), + HB_CODEPOINT_ENCODE3 (0x1F10, 0x0301, 0x1F14), HB_CODEPOINT_ENCODE3 (0x1F11, 0x0300, 0x1F13), + HB_CODEPOINT_ENCODE3 (0x1F11, 0x0301, 0x1F15), HB_CODEPOINT_ENCODE3 (0x1F18, 0x0300, 0x1F1A), + HB_CODEPOINT_ENCODE3 (0x1F18, 0x0301, 0x1F1C), HB_CODEPOINT_ENCODE3 (0x1F19, 0x0300, 0x1F1B), + HB_CODEPOINT_ENCODE3 (0x1F19, 0x0301, 0x1F1D), HB_CODEPOINT_ENCODE3 (0x1F20, 0x0300, 0x1F22), + HB_CODEPOINT_ENCODE3 (0x1F20, 0x0301, 0x1F24), HB_CODEPOINT_ENCODE3 (0x1F20, 0x0342, 0x1F26), + HB_CODEPOINT_ENCODE3 (0x1F20, 0x0345, 0x1F90), HB_CODEPOINT_ENCODE3 (0x1F21, 0x0300, 0x1F23), + HB_CODEPOINT_ENCODE3 (0x1F21, 0x0301, 0x1F25), HB_CODEPOINT_ENCODE3 (0x1F21, 0x0342, 0x1F27), + HB_CODEPOINT_ENCODE3 (0x1F21, 0x0345, 0x1F91), HB_CODEPOINT_ENCODE3 (0x1F22, 0x0345, 0x1F92), + HB_CODEPOINT_ENCODE3 (0x1F23, 0x0345, 0x1F93), HB_CODEPOINT_ENCODE3 (0x1F24, 0x0345, 0x1F94), + HB_CODEPOINT_ENCODE3 (0x1F25, 0x0345, 0x1F95), HB_CODEPOINT_ENCODE3 (0x1F26, 0x0345, 0x1F96), + HB_CODEPOINT_ENCODE3 (0x1F27, 0x0345, 0x1F97), HB_CODEPOINT_ENCODE3 (0x1F28, 0x0300, 0x1F2A), + HB_CODEPOINT_ENCODE3 (0x1F28, 0x0301, 0x1F2C), HB_CODEPOINT_ENCODE3 (0x1F28, 0x0342, 0x1F2E), + HB_CODEPOINT_ENCODE3 (0x1F28, 0x0345, 0x1F98), HB_CODEPOINT_ENCODE3 (0x1F29, 0x0300, 0x1F2B), + HB_CODEPOINT_ENCODE3 (0x1F29, 0x0301, 0x1F2D), HB_CODEPOINT_ENCODE3 (0x1F29, 0x0342, 0x1F2F), + HB_CODEPOINT_ENCODE3 (0x1F29, 0x0345, 0x1F99), HB_CODEPOINT_ENCODE3 (0x1F2A, 0x0345, 0x1F9A), + HB_CODEPOINT_ENCODE3 (0x1F2B, 0x0345, 0x1F9B), HB_CODEPOINT_ENCODE3 (0x1F2C, 0x0345, 0x1F9C), + HB_CODEPOINT_ENCODE3 (0x1F2D, 0x0345, 0x1F9D), HB_CODEPOINT_ENCODE3 (0x1F2E, 0x0345, 0x1F9E), + HB_CODEPOINT_ENCODE3 (0x1F2F, 0x0345, 0x1F9F), HB_CODEPOINT_ENCODE3 (0x1F30, 0x0300, 0x1F32), + HB_CODEPOINT_ENCODE3 (0x1F30, 0x0301, 0x1F34), HB_CODEPOINT_ENCODE3 (0x1F30, 0x0342, 0x1F36), + HB_CODEPOINT_ENCODE3 (0x1F31, 0x0300, 0x1F33), HB_CODEPOINT_ENCODE3 (0x1F31, 0x0301, 0x1F35), + HB_CODEPOINT_ENCODE3 (0x1F31, 0x0342, 0x1F37), HB_CODEPOINT_ENCODE3 (0x1F38, 0x0300, 0x1F3A), + HB_CODEPOINT_ENCODE3 (0x1F38, 0x0301, 0x1F3C), HB_CODEPOINT_ENCODE3 (0x1F38, 0x0342, 0x1F3E), + HB_CODEPOINT_ENCODE3 (0x1F39, 0x0300, 0x1F3B), HB_CODEPOINT_ENCODE3 (0x1F39, 0x0301, 0x1F3D), + HB_CODEPOINT_ENCODE3 (0x1F39, 0x0342, 0x1F3F), HB_CODEPOINT_ENCODE3 (0x1F40, 0x0300, 0x1F42), + HB_CODEPOINT_ENCODE3 (0x1F40, 0x0301, 0x1F44), HB_CODEPOINT_ENCODE3 (0x1F41, 0x0300, 0x1F43), + HB_CODEPOINT_ENCODE3 (0x1F41, 0x0301, 0x1F45), HB_CODEPOINT_ENCODE3 (0x1F48, 0x0300, 0x1F4A), + HB_CODEPOINT_ENCODE3 (0x1F48, 0x0301, 0x1F4C), HB_CODEPOINT_ENCODE3 (0x1F49, 0x0300, 0x1F4B), + HB_CODEPOINT_ENCODE3 (0x1F49, 0x0301, 0x1F4D), HB_CODEPOINT_ENCODE3 (0x1F50, 0x0300, 0x1F52), + HB_CODEPOINT_ENCODE3 (0x1F50, 0x0301, 0x1F54), HB_CODEPOINT_ENCODE3 (0x1F50, 0x0342, 0x1F56), + HB_CODEPOINT_ENCODE3 (0x1F51, 0x0300, 0x1F53), HB_CODEPOINT_ENCODE3 (0x1F51, 0x0301, 0x1F55), + HB_CODEPOINT_ENCODE3 (0x1F51, 0x0342, 0x1F57), HB_CODEPOINT_ENCODE3 (0x1F59, 0x0300, 0x1F5B), + HB_CODEPOINT_ENCODE3 (0x1F59, 0x0301, 0x1F5D), HB_CODEPOINT_ENCODE3 (0x1F59, 0x0342, 0x1F5F), + HB_CODEPOINT_ENCODE3 (0x1F60, 0x0300, 0x1F62), HB_CODEPOINT_ENCODE3 (0x1F60, 0x0301, 0x1F64), + HB_CODEPOINT_ENCODE3 (0x1F60, 0x0342, 0x1F66), HB_CODEPOINT_ENCODE3 (0x1F60, 0x0345, 0x1FA0), + HB_CODEPOINT_ENCODE3 (0x1F61, 0x0300, 0x1F63), HB_CODEPOINT_ENCODE3 (0x1F61, 0x0301, 0x1F65), + HB_CODEPOINT_ENCODE3 (0x1F61, 0x0342, 0x1F67), HB_CODEPOINT_ENCODE3 (0x1F61, 0x0345, 0x1FA1), + HB_CODEPOINT_ENCODE3 (0x1F62, 0x0345, 0x1FA2), HB_CODEPOINT_ENCODE3 (0x1F63, 0x0345, 0x1FA3), + HB_CODEPOINT_ENCODE3 (0x1F64, 0x0345, 0x1FA4), HB_CODEPOINT_ENCODE3 (0x1F65, 0x0345, 0x1FA5), + HB_CODEPOINT_ENCODE3 (0x1F66, 0x0345, 0x1FA6), HB_CODEPOINT_ENCODE3 (0x1F67, 0x0345, 0x1FA7), + HB_CODEPOINT_ENCODE3 (0x1F68, 0x0300, 0x1F6A), HB_CODEPOINT_ENCODE3 (0x1F68, 0x0301, 0x1F6C), + HB_CODEPOINT_ENCODE3 (0x1F68, 0x0342, 0x1F6E), HB_CODEPOINT_ENCODE3 (0x1F68, 0x0345, 0x1FA8), + HB_CODEPOINT_ENCODE3 (0x1F69, 0x0300, 0x1F6B), HB_CODEPOINT_ENCODE3 (0x1F69, 0x0301, 0x1F6D), + HB_CODEPOINT_ENCODE3 (0x1F69, 0x0342, 0x1F6F), HB_CODEPOINT_ENCODE3 (0x1F69, 0x0345, 0x1FA9), + HB_CODEPOINT_ENCODE3 (0x1F6A, 0x0345, 0x1FAA), HB_CODEPOINT_ENCODE3 (0x1F6B, 0x0345, 0x1FAB), + HB_CODEPOINT_ENCODE3 (0x1F6C, 0x0345, 0x1FAC), HB_CODEPOINT_ENCODE3 (0x1F6D, 0x0345, 0x1FAD), + HB_CODEPOINT_ENCODE3 (0x1F6E, 0x0345, 0x1FAE), HB_CODEPOINT_ENCODE3 (0x1F6F, 0x0345, 0x1FAF), + HB_CODEPOINT_ENCODE3 (0x1F70, 0x0345, 0x1FB2), HB_CODEPOINT_ENCODE3 (0x1F74, 0x0345, 0x1FC2), + HB_CODEPOINT_ENCODE3 (0x1F7C, 0x0345, 0x1FF2), HB_CODEPOINT_ENCODE3 (0x1FB6, 0x0345, 0x1FB7), + HB_CODEPOINT_ENCODE3 (0x1FBF, 0x0300, 0x1FCD), HB_CODEPOINT_ENCODE3 (0x1FBF, 0x0301, 0x1FCE), + HB_CODEPOINT_ENCODE3 (0x1FBF, 0x0342, 0x1FCF), HB_CODEPOINT_ENCODE3 (0x1FC6, 0x0345, 0x1FC7), + HB_CODEPOINT_ENCODE3 (0x1FF6, 0x0345, 0x1FF7), HB_CODEPOINT_ENCODE3 (0x1FFE, 0x0300, 0x1FDD), + HB_CODEPOINT_ENCODE3 (0x1FFE, 0x0301, 0x1FDE), HB_CODEPOINT_ENCODE3 (0x1FFE, 0x0342, 0x1FDF), + HB_CODEPOINT_ENCODE3 (0x2190, 0x0338, 0x219A), HB_CODEPOINT_ENCODE3 (0x2192, 0x0338, 0x219B), + HB_CODEPOINT_ENCODE3 (0x2194, 0x0338, 0x21AE), HB_CODEPOINT_ENCODE3 (0x21D0, 0x0338, 0x21CD), + HB_CODEPOINT_ENCODE3 (0x21D2, 0x0338, 0x21CF), HB_CODEPOINT_ENCODE3 (0x21D4, 0x0338, 0x21CE), + HB_CODEPOINT_ENCODE3 (0x2203, 0x0338, 0x2204), HB_CODEPOINT_ENCODE3 (0x2208, 0x0338, 0x2209), + HB_CODEPOINT_ENCODE3 (0x220B, 0x0338, 0x220C), HB_CODEPOINT_ENCODE3 (0x2223, 0x0338, 0x2224), + HB_CODEPOINT_ENCODE3 (0x2225, 0x0338, 0x2226), HB_CODEPOINT_ENCODE3 (0x223C, 0x0338, 0x2241), + HB_CODEPOINT_ENCODE3 (0x2243, 0x0338, 0x2244), HB_CODEPOINT_ENCODE3 (0x2245, 0x0338, 0x2247), + HB_CODEPOINT_ENCODE3 (0x2248, 0x0338, 0x2249), HB_CODEPOINT_ENCODE3 (0x224D, 0x0338, 0x226D), + HB_CODEPOINT_ENCODE3 (0x2261, 0x0338, 0x2262), HB_CODEPOINT_ENCODE3 (0x2264, 0x0338, 0x2270), + HB_CODEPOINT_ENCODE3 (0x2265, 0x0338, 0x2271), HB_CODEPOINT_ENCODE3 (0x2272, 0x0338, 0x2274), + HB_CODEPOINT_ENCODE3 (0x2273, 0x0338, 0x2275), HB_CODEPOINT_ENCODE3 (0x2276, 0x0338, 0x2278), + HB_CODEPOINT_ENCODE3 (0x2277, 0x0338, 0x2279), HB_CODEPOINT_ENCODE3 (0x227A, 0x0338, 0x2280), + HB_CODEPOINT_ENCODE3 (0x227B, 0x0338, 0x2281), HB_CODEPOINT_ENCODE3 (0x227C, 0x0338, 0x22E0), + HB_CODEPOINT_ENCODE3 (0x227D, 0x0338, 0x22E1), HB_CODEPOINT_ENCODE3 (0x2282, 0x0338, 0x2284), + HB_CODEPOINT_ENCODE3 (0x2283, 0x0338, 0x2285), HB_CODEPOINT_ENCODE3 (0x2286, 0x0338, 0x2288), + HB_CODEPOINT_ENCODE3 (0x2287, 0x0338, 0x2289), HB_CODEPOINT_ENCODE3 (0x2291, 0x0338, 0x22E2), + HB_CODEPOINT_ENCODE3 (0x2292, 0x0338, 0x22E3), HB_CODEPOINT_ENCODE3 (0x22A2, 0x0338, 0x22AC), + HB_CODEPOINT_ENCODE3 (0x22A8, 0x0338, 0x22AD), HB_CODEPOINT_ENCODE3 (0x22A9, 0x0338, 0x22AE), + HB_CODEPOINT_ENCODE3 (0x22AB, 0x0338, 0x22AF), HB_CODEPOINT_ENCODE3 (0x22B2, 0x0338, 0x22EA), + HB_CODEPOINT_ENCODE3 (0x22B3, 0x0338, 0x22EB), HB_CODEPOINT_ENCODE3 (0x22B4, 0x0338, 0x22EC), + HB_CODEPOINT_ENCODE3 (0x22B5, 0x0338, 0x22ED), HB_CODEPOINT_ENCODE3 (0x2ADD, 0x0338, 0x0000), + HB_CODEPOINT_ENCODE3 (0x3046, 0x3099, 0x3094), HB_CODEPOINT_ENCODE3 (0x304B, 0x3099, 0x304C), + HB_CODEPOINT_ENCODE3 (0x304D, 0x3099, 0x304E), HB_CODEPOINT_ENCODE3 (0x304F, 0x3099, 0x3050), + HB_CODEPOINT_ENCODE3 (0x3051, 0x3099, 0x3052), HB_CODEPOINT_ENCODE3 (0x3053, 0x3099, 0x3054), + HB_CODEPOINT_ENCODE3 (0x3055, 0x3099, 0x3056), HB_CODEPOINT_ENCODE3 (0x3057, 0x3099, 0x3058), + HB_CODEPOINT_ENCODE3 (0x3059, 0x3099, 0x305A), HB_CODEPOINT_ENCODE3 (0x305B, 0x3099, 0x305C), + HB_CODEPOINT_ENCODE3 (0x305D, 0x3099, 0x305E), HB_CODEPOINT_ENCODE3 (0x305F, 0x3099, 0x3060), + HB_CODEPOINT_ENCODE3 (0x3061, 0x3099, 0x3062), HB_CODEPOINT_ENCODE3 (0x3064, 0x3099, 0x3065), + HB_CODEPOINT_ENCODE3 (0x3066, 0x3099, 0x3067), HB_CODEPOINT_ENCODE3 (0x3068, 0x3099, 0x3069), + HB_CODEPOINT_ENCODE3 (0x306F, 0x3099, 0x3070), HB_CODEPOINT_ENCODE3 (0x306F, 0x309A, 0x3071), + HB_CODEPOINT_ENCODE3 (0x3072, 0x3099, 0x3073), HB_CODEPOINT_ENCODE3 (0x3072, 0x309A, 0x3074), + HB_CODEPOINT_ENCODE3 (0x3075, 0x3099, 0x3076), HB_CODEPOINT_ENCODE3 (0x3075, 0x309A, 0x3077), + HB_CODEPOINT_ENCODE3 (0x3078, 0x3099, 0x3079), HB_CODEPOINT_ENCODE3 (0x3078, 0x309A, 0x307A), + HB_CODEPOINT_ENCODE3 (0x307B, 0x3099, 0x307C), HB_CODEPOINT_ENCODE3 (0x307B, 0x309A, 0x307D), + HB_CODEPOINT_ENCODE3 (0x309D, 0x3099, 0x309E), HB_CODEPOINT_ENCODE3 (0x30A6, 0x3099, 0x30F4), + HB_CODEPOINT_ENCODE3 (0x30AB, 0x3099, 0x30AC), HB_CODEPOINT_ENCODE3 (0x30AD, 0x3099, 0x30AE), + HB_CODEPOINT_ENCODE3 (0x30AF, 0x3099, 0x30B0), HB_CODEPOINT_ENCODE3 (0x30B1, 0x3099, 0x30B2), + HB_CODEPOINT_ENCODE3 (0x30B3, 0x3099, 0x30B4), HB_CODEPOINT_ENCODE3 (0x30B5, 0x3099, 0x30B6), + HB_CODEPOINT_ENCODE3 (0x30B7, 0x3099, 0x30B8), HB_CODEPOINT_ENCODE3 (0x30B9, 0x3099, 0x30BA), + HB_CODEPOINT_ENCODE3 (0x30BB, 0x3099, 0x30BC), HB_CODEPOINT_ENCODE3 (0x30BD, 0x3099, 0x30BE), + HB_CODEPOINT_ENCODE3 (0x30BF, 0x3099, 0x30C0), HB_CODEPOINT_ENCODE3 (0x30C1, 0x3099, 0x30C2), + HB_CODEPOINT_ENCODE3 (0x30C4, 0x3099, 0x30C5), HB_CODEPOINT_ENCODE3 (0x30C6, 0x3099, 0x30C7), + HB_CODEPOINT_ENCODE3 (0x30C8, 0x3099, 0x30C9), HB_CODEPOINT_ENCODE3 (0x30CF, 0x3099, 0x30D0), + HB_CODEPOINT_ENCODE3 (0x30CF, 0x309A, 0x30D1), HB_CODEPOINT_ENCODE3 (0x30D2, 0x3099, 0x30D3), + HB_CODEPOINT_ENCODE3 (0x30D2, 0x309A, 0x30D4), HB_CODEPOINT_ENCODE3 (0x30D5, 0x3099, 0x30D6), + HB_CODEPOINT_ENCODE3 (0x30D5, 0x309A, 0x30D7), HB_CODEPOINT_ENCODE3 (0x30D8, 0x3099, 0x30D9), + HB_CODEPOINT_ENCODE3 (0x30D8, 0x309A, 0x30DA), HB_CODEPOINT_ENCODE3 (0x30DB, 0x3099, 0x30DC), + HB_CODEPOINT_ENCODE3 (0x30DB, 0x309A, 0x30DD), HB_CODEPOINT_ENCODE3 (0x30EF, 0x3099, 0x30F7), + HB_CODEPOINT_ENCODE3 (0x30F0, 0x3099, 0x30F8), HB_CODEPOINT_ENCODE3 (0x30F1, 0x3099, 0x30F9), + HB_CODEPOINT_ENCODE3 (0x30F2, 0x3099, 0x30FA), HB_CODEPOINT_ENCODE3 (0x30FD, 0x3099, 0x30FE), + HB_CODEPOINT_ENCODE3 (0xFB49, 0x05C1, 0x0000), HB_CODEPOINT_ENCODE3 (0xFB49, 0x05C2, 0x0000), + HB_CODEPOINT_ENCODE3 (0x105D2, 0x0307, 0x105C9), HB_CODEPOINT_ENCODE3 (0x105DA, 0x0307, 0x105E4), + HB_CODEPOINT_ENCODE3 (0x11099, 0x110BA, 0x1109A),HB_CODEPOINT_ENCODE3 (0x1109B, 0x110BA, 0x1109C), + HB_CODEPOINT_ENCODE3 (0x110A5, 0x110BA, 0x110AB),HB_CODEPOINT_ENCODE3 (0x11131, 0x11127, 0x1112E), + HB_CODEPOINT_ENCODE3 (0x11132, 0x11127, 0x1112F),HB_CODEPOINT_ENCODE3 (0x11347, 0x1133E, 0x1134B), + HB_CODEPOINT_ENCODE3 (0x11347, 0x11357, 0x1134C),HB_CODEPOINT_ENCODE3 (0x11382, 0x113C9, 0x11383), + HB_CODEPOINT_ENCODE3 (0x11384, 0x113BB, 0x11385),HB_CODEPOINT_ENCODE3 (0x1138B, 0x113C2, 0x1138E), + HB_CODEPOINT_ENCODE3 (0x11390, 0x113C9, 0x11391),HB_CODEPOINT_ENCODE3 (0x113C2, 0x113B8, 0x113C7), + HB_CODEPOINT_ENCODE3 (0x113C2, 0x113C2, 0x113C5),HB_CODEPOINT_ENCODE3 (0x113C2, 0x113C9, 0x113C8), + HB_CODEPOINT_ENCODE3 (0x114B9, 0x114B0, 0x114BC),HB_CODEPOINT_ENCODE3 (0x114B9, 0x114BA, 0x114BB), + HB_CODEPOINT_ENCODE3 (0x114B9, 0x114BD, 0x114BE),HB_CODEPOINT_ENCODE3 (0x115B8, 0x115AF, 0x115BA), + HB_CODEPOINT_ENCODE3 (0x115B9, 0x115AF, 0x115BB),HB_CODEPOINT_ENCODE3 (0x11935, 0x11930, 0x11938), + HB_CODEPOINT_ENCODE3 (0x1611E, 0x1611E, 0x16121),HB_CODEPOINT_ENCODE3 (0x1611E, 0x1611F, 0x16123), + HB_CODEPOINT_ENCODE3 (0x1611E, 0x16120, 0x16125),HB_CODEPOINT_ENCODE3 (0x1611E, 0x16129, 0x16122), + HB_CODEPOINT_ENCODE3 (0x16121, 0x1611F, 0x16126),HB_CODEPOINT_ENCODE3 (0x16121, 0x16120, 0x16128), + HB_CODEPOINT_ENCODE3 (0x16122, 0x1611F, 0x16127),HB_CODEPOINT_ENCODE3 (0x16129, 0x1611F, 0x16124), + HB_CODEPOINT_ENCODE3 (0x16D63, 0x16D67, 0x16D69),HB_CODEPOINT_ENCODE3 (0x16D67, 0x16D67, 0x16D68), + HB_CODEPOINT_ENCODE3 (0x16D69, 0x16D67, 0x16D6A), HB_CODEPOINT_ENCODE3 (0x1D157, 0x1D165, 0x0000), + HB_CODEPOINT_ENCODE3 (0x1D158, 0x1D165, 0x0000), HB_CODEPOINT_ENCODE3 (0x1D15F, 0x1D16E, 0x0000), + HB_CODEPOINT_ENCODE3 (0x1D15F, 0x1D16F, 0x0000), HB_CODEPOINT_ENCODE3 (0x1D15F, 0x1D170, 0x0000), + HB_CODEPOINT_ENCODE3 (0x1D15F, 0x1D171, 0x0000), HB_CODEPOINT_ENCODE3 (0x1D15F, 0x1D172, 0x0000), + HB_CODEPOINT_ENCODE3 (0x1D1B9, 0x1D165, 0x0000), HB_CODEPOINT_ENCODE3 (0x1D1BA, 0x1D165, 0x0000), + HB_CODEPOINT_ENCODE3 (0x1D1BB, 0x1D16E, 0x0000), HB_CODEPOINT_ENCODE3 (0x1D1BB, 0x1D16F, 0x0000), + HB_CODEPOINT_ENCODE3 (0x1D1BC, 0x1D16E, 0x0000), HB_CODEPOINT_ENCODE3 (0x1D1BC, 0x1D16F, 0x0000), }; #ifndef HB_OPTIMIZE_SIZE -static const uint8_t -_hb_ucd_u8[17612] = +#include + +static const uint8_t _hb_ucd_u8[19868]= { - 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 5, 5, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 5, 17, 15, 18, 19, 20, 21, 22, 23, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 24, 25, 26, 5, 27, 28, - 5, 29, 30, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 31, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 32, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 33, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 17, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 33, 41, 42, 43, 44, 45, - 46, 47, 48, 39, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 49, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 50, 17, 17, 17, 51, 17, 52, 53, 54, 55, 56, 57, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 58, 59, 59, 59, 59, 59, 59, 59, 59, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 17, 61, 62, 17, 63, 64, 65, - 66, 67, 68, 69, 70, 71, 17, 72, 73, 74, 75, 76, 77, 78, 79, 80, - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, - 17, 17, 17, 97, 98, 99,100,100,100,100,100,100,100,100,100,101, - 17, 17, 17, 17,102, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17,103, 17, 17,104,100,100,100,100,100,100,100,100,100, - 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, - 100,105,100,100,100,100,100,100, 17, 17,106,107,100,108,109,110, - 17, 17, 17, 17, 17, 17, 17,111, 17, 17, 17, 17,112,113,100,100, - 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,114, - 17,115,116,100,100,100,100,100,100,100,100,100,117,100,100,100, - 100,100,100,100,100,100,100,100,100,100,100,100,118, 39,119,120, - 121,122,123,124,125,126,127,128, 39, 39,129,100,100,100,100,130, - 131,132,133,100,134,135,100,136,137,138,100,100,139,140,141,100, - 142,143,144,145, 39, 39,146,147,148, 39,149,150,100,100,100,100, - 17, 17, 17, 17, 17, 17,151, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17,152,153, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,154, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,155, 17, 17,156,100, - 100,100,100,100,100,100,100,100, 17, 17,157,100,100,100,100,100, - 17, 17, 17,158, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17,159,100,100,100,100,100,100,100,100,100,100,100,100, - 160,161,100,100,100,100,100,100,100,100,100,100,100,100,100,100, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,162, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,163, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 27, 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, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 28, 26, 29, 30, 31, 32, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 33, 34, 34, 34, 34, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 26, 56, 57, 58, 58, 58, 58, 59, 26, 26, 60, 26, 26, 26, 26, 26, + 26, 61, 26, 62, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 63, 58, 58, 58, 26, 64, 65, 66, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 67, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 68, 69, 70, 58, 58, 58, 58, 71, 58, + 58, 58, 58, 58, 58, 58, 72, 73, 74, 75, 76, 77, 78, 79, 58, 80, + 81, 82, 83, 84, 85, 58, 86, 87, 88, 89, 78, 90, 91, 92, 58, 58, + 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, 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, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 93, 26, 26, 26, 26, 26, 26, 26, 26, 94, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 95, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 96, 26, 97, 58, 58, 58, 58, 26, 98, 58, 58, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 99, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,100, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 101, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,102, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,103, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, @@ -1148,496 +914,529 @@ _hb_ucd_u8[17612] = 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, - 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, - 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, - 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, - 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 44, 45, 16, 10, - 44, 44, 41, 46, 11, 47, 47, 11, 34, 11, 11, 11, 11, 11, 11, 11, - 11, 48, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, - 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 49, 34, 32, 34, 11, - 32, 50, 43, 43, 51, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, - 48, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 47, 52, 2, 2, 2, - 16, 16, 16, 16, 53, 54, 55, 56, 57, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 58, 59, 60, 43, 59, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 62, - 36, 63, 64, 44, 44, 44, 44, 44, 65, 65, 65, 8, 9, 66, 2, 67, - 43, 43, 43, 43, 43, 60, 68, 2, 69, 36, 36, 36, 36, 70, 43, 43, - 7, 7, 7, 7, 7, 2, 2, 36, 71, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 72, 43, 43, 43, 73, 50, 43, 43, 74, 75, 76, 43, 43, 36, - 7, 7, 7, 7, 7, 36, 77, 78, 2, 2, 2, 2, 2, 2, 2, 79, - 70, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 80, 62, 36, - 36, 36, 36, 43, 43, 43, 43, 43, 71, 44, 44, 44, 44, 44, 44, 44, - 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 70, 43, 43, - 43, 43, 40, 21, 2, 81, 57, 20, 36, 36, 36, 43, 43, 75, 43, 43, - 43, 43, 75, 43, 75, 43, 43, 44, 2, 2, 2, 2, 2, 2, 2, 64, - 36, 36, 36, 36, 70, 43, 44, 64, 36, 36, 36, 36, 36, 61, 44, 44, - 36, 36, 36, 36, 82, 36, 36, 61, 65, 44, 44, 57, 43, 43, 43, 43, - 36, 36, 36, 36, 83, 43, 43, 43, 43, 84, 43, 43, 43, 43, 43, 43, - 43, 85, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 85, 71, 86, - 87, 43, 43, 43, 85, 86, 87, 86, 70, 43, 43, 43, 36, 36, 36, 36, - 36, 43, 2, 7, 7, 7, 7, 7, 88, 36, 36, 36, 36, 36, 36, 36, - 70, 86, 62, 36, 36, 36, 61, 62, 61, 62, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 61, 36, 36, 36, 61, 61, 44, 36, 36, 44, 71, 86, - 87, 43, 80, 89, 90, 89, 87, 61, 44, 44, 44, 89, 44, 44, 36, 62, - 36, 43, 44, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 56, 63, 80, - 57, 85, 62, 36, 36, 61, 44, 62, 61, 36, 62, 61, 36, 44, 80, 86, - 87, 80, 44, 57, 80, 57, 43, 44, 57, 44, 44, 44, 62, 36, 61, 61, - 44, 44, 44, 7, 7, 7, 7, 7, 43, 36, 70, 64, 44, 44, 44, 44, - 57, 85, 62, 36, 36, 36, 36, 62, 36, 62, 36, 36, 36, 36, 36, 36, - 61, 36, 62, 36, 36, 44, 71, 86, 87, 43, 43, 57, 85, 89, 87, 44, - 61, 44, 44, 44, 44, 44, 44, 44, 66, 44, 44, 44, 62, 43, 43, 43, - 57, 86, 62, 36, 36, 36, 61, 62, 61, 36, 62, 36, 36, 44, 71, 87, - 87, 43, 80, 89, 90, 89, 87, 44, 44, 44, 57, 85, 44, 44, 36, 62, - 78, 27, 27, 27, 44, 44, 44, 44, 44, 71, 62, 36, 36, 61, 44, 36, - 61, 36, 36, 44, 62, 61, 61, 36, 44, 62, 61, 44, 36, 61, 44, 36, - 36, 36, 36, 36, 36, 44, 44, 86, 85, 90, 44, 86, 90, 86, 87, 44, - 61, 44, 44, 89, 44, 44, 44, 44, 27, 91, 67, 67, 56, 92, 44, 44, - 85, 86, 71, 36, 36, 36, 61, 36, 61, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 44, 71, 43, 85, 86, 90, 43, 80, 43, 43, 44, - 44, 44, 57, 80, 36, 61, 62, 44, 44, 44, 44, 93, 27, 27, 27, 91, - 70, 86, 72, 36, 36, 36, 61, 36, 36, 36, 62, 36, 36, 44, 71, 87, - 86, 86, 90, 85, 90, 86, 43, 44, 44, 44, 89, 90, 44, 44, 62, 61, - 62, 94, 44, 44, 44, 44, 44, 44, 43, 86, 36, 36, 36, 36, 61, 36, - 36, 36, 36, 36, 36, 70, 71, 86, 87, 43, 80, 86, 90, 86, 87, 77, - 44, 44, 36, 94, 27, 27, 27, 95, 27, 27, 27, 27, 91, 36, 36, 36, - 57, 86, 62, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 36, 36, 36, - 36, 62, 36, 36, 36, 36, 62, 44, 36, 36, 36, 61, 44, 80, 44, 89, - 86, 43, 80, 80, 86, 86, 86, 86, 44, 86, 64, 44, 44, 44, 44, 44, - 62, 36, 36, 36, 36, 36, 36, 36, 70, 36, 43, 43, 43, 80, 44, 96, - 36, 36, 36, 75, 43, 43, 43, 60, 7, 7, 7, 7, 7, 2, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 62, 61, 61, 36, 36, 61, 36, 36, - 36, 36, 62, 62, 36, 36, 36, 36, 70, 36, 43, 43, 43, 43, 71, 44, - 36, 36, 61, 81, 43, 43, 43, 80, 7, 7, 7, 7, 7, 44, 36, 36, - 77, 67, 2, 2, 2, 2, 2, 2, 2, 97, 97, 67, 43, 67, 67, 67, - 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 50, 50, 50, 4, 4, 86, - 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, - 57, 43, 43, 43, 43, 43, 43, 85, 43, 43, 60, 43, 36, 36, 70, 43, - 43, 43, 43, 43, 57, 43, 43, 43, 43, 43, 43, 43, 43, 43, 80, 67, - 67, 67, 67, 76, 67, 67, 92, 67, 2, 2, 97, 67, 21, 64, 44, 44, - 36, 36, 36, 36, 36, 94, 87, 43, 85, 43, 43, 43, 87, 85, 87, 71, - 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 86, 43, 36, 36, 43, - 71, 86, 98, 94, 86, 86, 86, 36, 70, 43, 71, 36, 36, 36, 36, 36, - 36, 85, 87, 85, 86, 86, 87, 94, 7, 7, 7, 7, 7, 86, 87, 67, - 11, 11, 11, 48, 44, 44, 48, 44, 16, 16, 16, 16, 16, 53, 45, 16, - 36, 36, 36, 36, 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, - 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, 36, 36, 36, 36, - 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 57, 43, - 2, 2, 2, 2, 99, 27, 27, 27, 27, 27, 27, 27, 27, 27,100, 44, - 67, 67, 67, 67, 67, 44, 44, 44, 11, 11, 11, 44, 16, 16, 16, 44, - 101, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 72, - 102, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,103,104, 44, - 36, 36, 36, 36, 36, 63, 2,105,106, 36, 36, 36, 61, 44, 44, 44, - 36, 43, 85, 44, 44, 44, 44, 62, 36, 43,107, 64, 44, 44, 44, 44, - 36, 43, 44, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 61, 36, - 61, 43, 44, 44, 44, 44, 44, 44, 36, 36, 43, 87, 43, 43, 43, 86, - 86, 86, 86, 85, 87, 43, 43, 43, 43, 43, 2, 88, 2, 66, 70, 44, - 7, 7, 7, 7, 7, 44, 44, 44, 27, 27, 27, 27, 27, 44, 44, 44, - 2, 2, 2,108, 2, 59, 43, 84, 36, 83, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 61, 44, 44, 44, 36, 36, 70, 71, 36, 36, 36, 36, - 36, 36, 36, 36, 70, 61, 44, 44, 36, 36, 36, 44, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 36, 36, 61, 43, 85, 86, 87, 85, 86, 44, 44, - 86, 85, 86, 86, 87, 43, 44, 44, 92, 44, 2, 7, 7, 7, 7, 7, - 36, 36, 36, 36, 36, 36, 36, 44, 36, 36, 61, 44, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 36, 44, 44, 36, 36, 36, 36, 36, 44, 44, 44, - 7, 7, 7, 7, 7,100, 44, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 36, 36, 36, 70, 85, 87, 44, 2, 36, 36, 94, 85, 43, 43, 43, 80, - 85, 85, 87, 43, 43, 43, 85, 86, 86, 87, 43, 43, 43, 43, 80, 57, - 2, 2, 2, 88, 2, 2, 2, 44, 43, 43, 43, 43, 43, 43, 43,109, - 43, 43, 43, 43, 43, 43, 43, 80, 43, 43, 98, 36, 36, 36, 36, 36, - 36, 36, 85, 43, 43, 85, 85, 86, 86, 85, 98, 36, 36, 36, 61, 2, - 97, 67, 67, 67, 67, 50, 43, 43, 43, 43, 67, 67, 67, 67, 21, 2, - 43, 98, 36, 36, 36, 36, 36, 36, 94, 43, 43, 86, 43, 87, 43, 36, - 36, 36, 36, 85, 43, 86, 87, 87, 43, 86, 44, 44, 44, 44, 2, 2, - 36, 36, 86, 86, 86, 86, 43, 43, 43, 43, 86, 43, 44, 93, 2, 2, - 7, 7, 7, 7, 7, 44, 62, 36, 36, 36, 36, 36, 40, 40, 40, 2, - 16, 16, 16, 16, 34,110, 44, 44, 11, 11, 11, 11, 11, 47, 48, 11, - 2, 2, 2, 2, 44, 44, 44, 44, 43, 60, 43, 43, 43, 43, 43, 43, - 85, 43, 43, 43, 71, 36, 70, 36, 36, 36, 71, 94, 43, 61, 44, 44, - 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 45, 16, 16, - 16, 16, 16, 16, 45, 16, 16, 16, 16, 16, 16, 16, 16,111, 40, 40, - 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, - 16, 16, 16, 44, 11, 11, 11, 44, 16, 16, 16, 16, 48, 48, 48, 48, - 16, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, 16,112,112,112,112, - 16, 16,110, 16, 11, 11,113,114, 41, 16,110, 16, 11, 11,113, 41, - 16, 16, 44, 16, 11, 11,115, 41, 16, 16, 16, 16, 11, 11,116, 41, - 44, 16,110, 16, 11, 11,113,117,118,118,118,118,118,119, 65, 65, - 120,120,120, 2,121,122,121,122, 2, 2, 2, 2,123, 65, 65,124, - 2, 2, 2, 2,125,126, 2,127,128, 2,129,130, 2, 2, 2, 2, - 2, 9,128, 2, 2, 2, 2,131, 65, 65,132, 65, 65, 65, 65, 65, - 133, 44, 27, 27, 27, 8,129,134, 27, 27, 27, 27, 27, 8,129,104, - 40, 40, 40, 40, 40, 40, 81, 44, 20, 20, 20, 20, 20, 20, 20, 20, - 135, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43,136, 51, - 109, 51,109, 43, 43, 43, 43, 43, 80, 44, 44, 44, 44, 44, 44, 44, - 67,137, 67,138, 67, 34, 11, 16, 11, 32,138, 67, 49, 11, 11, 67, - 67, 67,137,137,137, 11, 11,139, 11, 11, 35, 36, 39, 67, 16, 11, - 8, 8, 49, 16, 16, 26, 67,140, 27, 27, 27, 27, 27, 27, 27, 27, - 105,105,105,105,105,105,105,105,105,141,142,105,143, 67, 44, 44, - 8, 8,144, 67, 67, 8, 67, 67,144, 26, 67,144, 67, 67, 67,144, - 67, 67, 67, 67, 67, 67, 67, 8, 67,144,144, 67, 67, 67, 67, 67, - 67, 67, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 67, 67, 67, 67, 4, 4, 67, 67, 8, 67, 67, 67,145,146, 67, 67, - 67, 67, 67, 67, 67, 67,144, 67, 67, 67, 67, 67, 67, 26, 8, 8, - 8, 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, - 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 92, 44, 44, - 27, 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, - 67, 67, 67, 26, 67, 67, 67, 67, 26, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 8, 8, 8, 8, 67, 67, 67, 67, 67, 67, 67, 26, - 67, 67, 67, 67, 4, 4, 4, 4, 4, 4, 4, 27, 27, 27, 27, 27, - 27, 27, 67, 67, 67, 67, 67, 67, 8, 8,129,147, 8, 8, 8, 8, - 8, 8, 8, 4, 4, 4, 4, 4, 8,129,148,148,148,148,148,148, - 148,148,148,148,147, 8, 8, 8, 8, 8, 8, 8, 4, 4, 8, 8, - 8, 8, 8, 8, 8, 8, 4, 8, 8, 8,144, 26, 8, 8,144, 67, - 67, 67, 44, 67, 67, 67, 67, 67, 67, 67, 67, 55, 67, 67, 67, 67, - 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 40, 11, - 32, 32,140, 67, 67,138, 34,149, 43, 32, 44, 44, 93, 2, 99, 2, - 16, 16, 16,150, 44, 44,150, 44, 36, 36, 36, 36, 44, 44, 44, 52, - 64, 44, 44, 44, 44, 44, 44, 57, 36, 36, 36, 61, 44, 44, 44, 44, - 36, 36, 36, 61, 36, 36, 36, 61, 2,121,121, 2,125,126,121, 2, - 2, 2, 2, 6, 2,108,121, 2,121, 4, 4, 4, 4, 2, 2, 88, - 2, 2, 2, 2, 2,120, 2, 2,108,151, 2, 2, 2, 2, 2, 2, - 67, 2,152,148,148,148,153, 44, 67, 67, 67, 67, 67, 55, 67, 67, - 67, 67, 44, 44, 44, 44, 44, 44, 67, 67, 67, 44, 44, 44, 44, 44, - 1, 2,154,155, 4, 4, 4, 4, 4, 67, 4, 4, 4, 4,156,157, - 158,105,105,105,105, 43, 43, 86,159, 40, 40, 67,105,160, 63, 67, - 36, 36, 36, 61, 57,161,162, 69, 36, 36, 36, 36, 36, 63, 40, 69, - 44, 44, 62, 36, 36, 36, 36, 36, 67, 27, 27, 67, 67, 67, 67, 67, - 67, 67, 67, 44, 44, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, 92, - 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, 27, - 163, 27, 27, 27, 27, 27, 27, 27, 36, 36, 83, 36, 36, 36, 36, 36, - 67, 67, 67, 92, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36,164, 2, - 7, 7, 7, 7, 7, 36, 44, 44, 32, 32, 32, 32, 32, 32, 32, 70, - 51,165, 43, 43, 43, 43, 43, 88, 32, 32, 32, 32, 32, 32, 40, 43, - 36, 36, 36,105,105,105,105,105, 43, 2, 2, 2, 44, 44, 44, 44, - 41, 41, 41,162, 40, 40, 40, 40, 41, 32, 32, 32, 32, 32, 32, 32, - 16, 32, 32, 32, 32, 32, 32, 32, 45, 16, 16, 16, 34, 34, 34, 32, - 32, 32, 32, 32, 42,166, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, + 16, 16, 36, 16, 16, 16, 16, 16, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 40, 40, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, + 39, 39, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, + 42, 42, 42, 42, 42, 42, 42, 42, 32, 32, 41, 32, 43, 44, 16, 10, + 43, 43, 40, 45, 11, 46, 46, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 47, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 48, 34, 32, 34, 11, + 32, 49, 42, 42, 50, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 47, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 46, 51, 2, 2, 2, + 16, 16, 16, 16, 52, 53, 54, 55, 56, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 57, 58, 59, 42, 58, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, 61, + 36, 62, 63, 43, 43, 43, 43, 43, 64, 64, 64, 8, 9, 65, 2, 66, + 42, 42, 42, 42, 42, 59, 67, 2, 68, 36, 36, 36, 36, 69, 42, 42, + 7, 7, 7, 7, 7, 2, 2, 36, 70, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 71, 42, 42, 42, 72, 49, 42, 42, 73, 74, 75, 42, 42, 36, + 7, 7, 7, 7, 7, 36, 76, 77, 2, 2, 2, 2, 2, 2, 2, 78, + 69, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 79, 61, 36, + 36, 36, 36, 42, 42, 42, 42, 42, 70, 43, 43, 43, 43, 43, 43, 43, + 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 69, 42, 42, + 42, 42, 39, 21, 2, 80, 56, 20, 36, 36, 36, 42, 42, 74, 42, 42, + 42, 42, 74, 42, 74, 42, 42, 43, 2, 2, 2, 2, 2, 2, 2, 63, + 36, 36, 36, 36, 69, 42, 43, 63, 36, 36, 36, 36, 36, 60, 43, 43, + 36, 36, 36, 36, 81, 36, 36, 36, 64, 43, 43, 56, 42, 42, 42, 42, + 36, 36, 36, 36, 82, 42, 42, 42, 42, 83, 42, 42, 42, 42, 42, 42, + 42, 84, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 84, 70, 85, + 86, 42, 42, 42, 84, 85, 86, 85, 69, 42, 42, 42, 36, 36, 36, 36, + 36, 42, 2, 7, 7, 7, 7, 7, 87, 36, 36, 36, 36, 36, 36, 36, + 69, 85, 61, 36, 36, 36, 60, 61, 60, 61, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 60, 36, 36, 36, 60, 60, 43, 36, 36, 43, 70, 85, + 86, 42, 79, 88, 89, 88, 86, 60, 43, 43, 43, 88, 43, 43, 36, 61, + 36, 42, 43, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 55, 62, 79, + 56, 84, 61, 36, 36, 60, 43, 61, 60, 36, 61, 60, 36, 43, 79, 85, + 86, 79, 43, 56, 79, 56, 42, 43, 56, 43, 43, 43, 61, 36, 60, 60, + 43, 43, 43, 7, 7, 7, 7, 7, 42, 36, 69, 63, 43, 43, 43, 43, + 56, 84, 61, 36, 36, 36, 36, 61, 36, 61, 36, 36, 36, 36, 36, 36, + 60, 36, 61, 36, 36, 43, 70, 85, 86, 42, 42, 56, 84, 88, 86, 43, + 60, 43, 43, 43, 43, 43, 43, 43, 65, 43, 43, 43, 61, 42, 42, 42, + 56, 85, 61, 36, 36, 36, 60, 61, 60, 36, 61, 36, 36, 43, 70, 86, + 86, 42, 79, 88, 89, 88, 86, 43, 43, 43, 56, 84, 43, 43, 36, 61, + 77, 27, 27, 27, 43, 43, 43, 43, 43, 70, 61, 36, 36, 60, 43, 36, + 60, 36, 36, 43, 61, 60, 60, 36, 43, 61, 60, 43, 36, 60, 43, 36, + 36, 36, 36, 36, 36, 43, 43, 85, 84, 89, 43, 85, 89, 85, 86, 43, + 60, 43, 43, 88, 43, 43, 43, 43, 27, 90, 66, 66, 55, 91, 43, 43, + 84, 85, 70, 36, 36, 36, 60, 36, 60, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 43, 70, 42, 84, 85, 89, 42, 79, 42, 42, 43, + 43, 43, 56, 79, 36, 60, 36, 43, 43, 43, 43, 92, 27, 27, 27, 90, + 69, 85, 71, 36, 36, 36, 60, 36, 36, 36, 61, 36, 36, 43, 70, 86, + 85, 85, 89, 84, 89, 85, 42, 43, 43, 43, 88, 89, 43, 43, 36, 60, + 61, 93, 43, 43, 43, 43, 43, 43, 42, 85, 36, 36, 36, 36, 60, 36, + 36, 36, 36, 36, 36, 69, 70, 85, 86, 42, 79, 85, 89, 85, 86, 76, + 43, 43, 36, 93, 27, 27, 27, 94, 27, 27, 27, 27, 90, 36, 36, 36, + 56, 85, 61, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, 36, 36, 36, + 36, 61, 36, 36, 36, 36, 61, 43, 36, 36, 36, 60, 43, 79, 43, 88, + 85, 42, 79, 79, 85, 85, 85, 85, 43, 85, 63, 43, 43, 43, 43, 43, + 61, 36, 36, 36, 36, 36, 36, 36, 69, 36, 42, 42, 42, 79, 43, 95, + 36, 36, 36, 74, 42, 42, 42, 59, 7, 7, 7, 7, 7, 2, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 61, 60, 60, 36, 36, 60, 36, 36, + 36, 36, 61, 61, 36, 36, 36, 36, 69, 36, 42, 42, 42, 42, 70, 43, + 36, 36, 60, 80, 42, 42, 42, 79, 7, 7, 7, 7, 7, 43, 36, 36, + 76, 66, 2, 2, 2, 2, 2, 2, 2, 96, 96, 66, 42, 66, 66, 66, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 49, 49, 49, 4, 4, 85, + 36, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, + 56, 42, 42, 42, 42, 42, 42, 84, 42, 42, 59, 42, 36, 36, 69, 42, + 42, 42, 42, 42, 56, 42, 42, 42, 42, 42, 42, 42, 42, 42, 79, 66, + 66, 66, 66, 75, 66, 66, 91, 66, 2, 2, 96, 66, 21, 63, 43, 43, + 36, 36, 36, 36, 36, 93, 86, 42, 84, 42, 42, 42, 86, 84, 86, 70, + 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 85, 42, 36, 36, 42, + 70, 85, 97, 93, 85, 85, 85, 36, 69, 42, 70, 36, 36, 36, 36, 36, + 36, 84, 86, 84, 85, 85, 86, 93, 7, 7, 7, 7, 7, 85, 86, 66, + 11, 11, 11, 47, 43, 43, 47, 43, 16, 16, 16, 16, 16, 52, 44, 16, + 36, 36, 36, 36, 60, 36, 36, 43, 36, 36, 36, 60, 60, 36, 36, 43, + 60, 36, 36, 43, 36, 36, 36, 60, 60, 36, 36, 43, 36, 36, 36, 36, + 36, 36, 36, 60, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 56, 42, + 2, 2, 2, 2, 98, 27, 27, 27, 27, 27, 27, 27, 27, 27, 99, 43, + 66, 66, 66, 66, 66, 43, 43, 43, 11, 11, 11, 43, 16, 16, 16, 43, + 100, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 76, 71, + 101, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,102,103, 43, + 36, 36, 36, 36, 36, 62, 2,104,105, 36, 36, 36, 60, 43, 43, 43, + 36, 42, 84, 43, 43, 43, 43, 61, 36, 42,106, 63, 43, 43, 43, 43, + 36, 42, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 60, 36, + 60, 42, 43, 43, 43, 43, 43, 43, 36, 36, 42, 86, 42, 42, 42, 85, + 85, 85, 85, 84, 86, 42, 42, 42, 42, 42, 2, 87, 2, 65, 69, 43, + 7, 7, 7, 7, 7, 43, 43, 43, 27, 27, 27, 27, 27, 43, 43, 43, + 2, 2, 2,107, 2, 58, 42, 83, 36, 82, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 60, 43, 43, 43, 36, 36, 69, 70, 36, 36, 36, 36, + 36, 36, 36, 36, 69, 60, 43, 43, 36, 36, 36, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 60, 42, 84, 85, 86, 84, 85, 43, 43, + 85, 84, 85, 85, 86, 42, 43, 43, 91, 43, 2, 7, 7, 7, 7, 7, + 36, 36, 36, 36, 36, 36, 36, 43, 36, 36, 60, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 43, 43, 36, 36, 36, 36, 36, 43, 43, 43, + 7, 7, 7, 7, 7, 99, 43, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 36, 36, 36, 69, 84, 86, 43, 2, 36, 36, 93, 84, 42, 42, 42, 79, + 84, 84, 86, 42, 42, 42, 84, 85, 85, 86, 42, 42, 42, 42, 79, 56, + 2, 2, 2, 87, 2, 2, 2, 43, 42, 42, 42, 42, 42, 42, 42,108, + 42, 42, 42, 42, 42, 42, 42, 43, 42, 42, 42, 42, 42, 42, 43, 43, + 42, 42, 97, 36, 36, 36, 36, 36, 36, 36, 84, 42, 42, 84, 84, 85, + 85, 84, 97, 36, 36, 36, 60, 2, 96, 66, 66, 66, 66, 49, 42, 42, + 42, 42, 66, 66, 66, 66, 21, 2, 42, 97, 36, 36, 36, 36, 36, 36, + 93, 42, 42, 85, 42, 86, 42, 36, 36, 36, 36, 84, 42, 85, 86, 86, + 42, 85, 43, 43, 43, 43, 2, 2, 36, 36, 85, 85, 85, 85, 42, 42, + 42, 42, 85, 42, 43, 92, 2, 2, 7, 7, 7, 7, 7, 43, 61, 36, + 36, 36, 36, 36, 39, 39, 39, 2, 16, 16, 16, 16, 34,109, 43, 43, + 11, 11, 11, 11, 11, 46, 47, 11, 2, 2, 2, 2, 43, 43, 43, 43, + 42, 59, 42, 42, 42, 42, 42, 42, 84, 42, 42, 42, 70, 36, 69, 36, + 36, 36, 70, 93, 42, 60, 43, 43, 16, 16, 16, 16, 16, 16, 39, 39, + 39, 39, 39, 39, 39, 44, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, + 16, 16, 16, 16, 16,110, 39, 39, 32, 32, 32, 16, 16, 16, 16, 32, + 16, 16, 16, 16, 11, 11, 11, 11, 16, 16, 16, 43, 11, 11, 11, 43, + 16, 16, 16, 16, 47, 47, 47, 47, 16, 16, 16, 16, 16, 16, 16, 43, + 16, 16, 16, 16,111,111,111,111, 16, 16,109, 16, 11, 11,112,113, + 40, 16,109, 16, 11, 11,112, 40, 16, 16, 43, 16, 11, 11,114, 40, + 16, 16, 16, 16, 11, 11,115, 40, 43, 16,109, 16, 11, 11,112,116, + 117,117,117,117,117,118, 64, 64,119,119,119, 2,120,121,120,121, + 2, 2, 2, 2,122, 64, 64,123, 2, 2, 2, 2,124,125, 2,126, + 127, 2,128,129, 2, 2, 2, 2, 2, 9,127, 2, 2, 2, 2,130, + 64, 64,131, 64, 64, 64, 64, 64,132, 43, 27, 27, 27, 8,128,133, + 27, 27, 27, 27, 27, 8,128,103, 39, 39, 39, 39, 39, 39, 80, 43, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 43, 43, 43, 43, 43, 43, 43, + 42, 42, 42, 42, 42, 42,134, 50,108, 50,108, 42, 42, 42, 42, 42, + 79, 43, 43, 43, 43, 43, 43, 43, 66,135, 66,136, 66, 34, 11, 16, + 11, 32,136, 66, 48, 11, 11, 66, 66, 66,135,135,135, 11, 11,137, + 11, 11, 35, 36,138, 66, 16, 11, 8, 8, 48, 16, 16, 26, 66,139, + 27, 27, 27, 27, 27, 27, 27, 27,104,104,104,104,104,104,104,104, + 104,140,141,104,142, 66, 43, 43, 8, 8,143, 66, 66, 8, 66, 66, + 143, 26, 66,143, 66, 66, 66,143, 66, 66, 66, 66, 66, 66, 66, 8, + 66,143,143, 66, 66, 66, 66, 66, 66, 66, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 66, 66, 66, 66, 4, 4, 66, 66, + 8, 66, 66, 66,144,145, 66, 66, 66, 66, 66, 66, 66, 66,143, 66, + 66, 66, 66, 66, 66, 26, 8, 8, 8, 8, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 8, 8, 8, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 91, 43, 43, 27, 27, 27, 27, 27, 27, 66, 66, + 66, 66, 66, 66, 66, 27, 27, 27, 66, 66, 66, 26, 66, 66, 66, 66, + 26, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 8, 8, 8, 8, + 66, 66, 66, 66, 66, 66, 66, 26, 66, 66, 66, 66, 4, 4, 4, 4, + 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 66, 66, 66, 66, 66, 66, + 8, 8,128,146, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, + 8,128,147,147,147,147,147,147,147,147,147,147,146, 8, 8, 8, + 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, + 8, 8,143, 26, 8, 8,143, 66, 66, 66, 43, 66, 66, 66, 66, 66, + 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 39, 11, + 32, 32,139, 66, 66,136, 34,148, 42, 32, 43, 43, 92, 2, 98, 2, + 16, 16, 16,149, 43, 43,149, 43, 36, 36, 36, 36, 43, 43, 43, 51, + 63, 43, 43, 43, 43, 43, 43, 56, 36, 36, 36, 60, 43, 43, 43, 43, + 36, 36, 36, 60, 36, 36, 36, 60, 2,120,120, 2,124,125,120, 2, + 2, 2, 2, 6, 2,107,120, 2,120, 4, 4, 4, 4, 2, 2, 87, + 2, 2, 2, 2, 2,119, 2, 2,107,150, 2, 2, 2, 2, 2, 2, + 66, 2,151,147,147,147,152, 43, 66, 66, 66, 66, 66, 54, 66, 66, + 66, 66, 43, 43, 43, 43, 43, 43, 66, 66, 66, 43, 43, 43, 43, 43, + 1, 2,153,154, 4, 4, 4, 4, 4, 66, 4, 4, 4, 4,155,156, + 157,104,104,104,104, 42, 42, 85,158, 39, 39, 66,104,159, 62, 66, + 36, 36, 36, 60, 56,160,161, 68, 36, 36, 36, 36, 36, 62, 39, 68, + 43, 43, 61, 36, 36, 36, 36, 36, 66, 27, 27, 66, 66, 66, 66, 66, + 66, 66, 66, 43, 43, 43, 43, 54, 66, 66, 66, 66, 66, 66, 66, 91, + 27, 27, 27, 27, 27, 66, 66, 66, 66, 66, 66, 66, 27, 27, 27, 27, + 162, 27, 27, 27, 27, 27, 27, 27, 36, 36, 82, 36, 36, 36, 36, 36, + 66, 66, 66, 91, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36,163, 2, + 7, 7, 7, 7, 7, 36, 43, 43, 32, 32, 32, 32, 32, 32, 32, 69, + 50,164, 42, 42, 42, 42, 42, 87, 32, 32, 32, 32, 32, 32, 39, 42, + 36, 36, 36,104,104,104,104,104, 42, 2, 2, 2, 43, 43, 43, 43, + 40, 40, 40,161, 39, 39, 39, 39, 40, 32, 32, 32, 32, 32, 32, 32, + 16, 32, 32, 32, 32, 32, 32, 32, 44, 16, 16, 16, 34, 34, 34, 32, + 32, 32, 32, 32, 41,165, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, - 32, 32, 11, 11, 34, 34, 32, 44, 32,150,150, 32, 32, 32, 47, 44, - 44, 40,167, 35, 40, 35, 36, 36, 36, 71, 36, 71, 36, 70, 36, 36, - 36, 94, 87, 85, 67, 67, 80, 44, 27, 27, 27, 67,168, 44, 44, 44, - 36, 36, 2, 2, 44, 44, 44, 44, 86, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 86, 86, 86, 86, 86, 86, 86, 86, 43, 44, 44, 44, 44, 2, - 43, 36, 36, 36, 2, 72, 72, 70, 36, 36, 36, 43, 43, 43, 43, 2, - 36, 36, 36, 70, 43, 43, 43, 43, 43, 86, 44, 44, 44, 44, 44, 93, - 36, 70, 86, 43, 43, 86, 43, 86,107, 2, 2, 2, 2, 2, 2, 52, - 7, 7, 7, 7, 7, 44, 44, 2, 36, 36, 70, 69, 36, 36, 36, 36, - 7, 7, 7, 7, 7, 36, 36, 61, 36, 36, 36, 36, 70, 43, 43, 85, - 87, 85, 87, 80, 44, 44, 44, 44, 36, 70, 36, 36, 36, 36, 85, 44, - 7, 7, 7, 7, 7, 44, 2, 2, 69, 36, 36, 77, 67, 94, 85, 36, - 71, 43, 71, 70, 71, 36, 36, 43, 70, 61, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 62, 83, 2, 36, 36, 36, 36, 36, 94, 43, 86, - 2, 83,169, 80, 44, 44, 44, 44, 62, 36, 36, 61, 62, 36, 36, 61, - 62, 36, 36, 61, 44, 44, 44, 44, 16, 16, 16, 16, 16,114, 40, 40, - 16, 16, 16, 16,111, 41, 44, 44, 36, 94, 87, 86, 85,107, 87, 44, - 36, 36, 44, 44, 44, 44, 44, 44, 36, 36, 36, 61, 44, 62, 36, 36, - 170,170,170,170,170,170,170,170,171,171,171,171,171,171,171,171, - 16, 16, 16,110, 44, 44, 44, 44, 44,150, 16, 16, 44, 44, 62, 71, - 36, 36, 36, 36,172, 36, 36, 36, 36, 36, 36, 61, 36, 36, 61, 61, - 36, 62, 61, 36, 36, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, - 41,117, 44, 44, 44, 44, 44, 44, 44, 62, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36,148, 44, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 44, 44, 44, 55, 36, 36, 36, 36, 36, 36,168, 67, - 2, 2, 2,152,130, 44, 44, 44, 6,173,174,148,148,148,148,148, - 148,148,130,152,130, 2,127,175, 2, 64, 2, 2,156,148,148,130, - 2,176, 8,177, 66, 2, 44, 44, 36, 36, 61, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 61, 79, 93, 2, 3, 2, 4, 5, 6, 2, - 16, 16, 16, 16, 16, 17, 18,129,130, 4, 2, 36, 36, 36, 36, 36, - 69, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, - 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 61, 44, - 20,178, 56,135, 26, 8,144, 92, 44, 44, 44, 44, 79, 65, 67, 44, - 36, 36, 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 61, 36, 62, - 2, 64, 44,179, 27, 27, 27, 27, 27, 27, 44, 55, 67, 67, 67, 67, - 105,105,143, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 27, 67, 92, - 67, 67, 67, 67, 67, 67, 92, 44, 92, 44, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 67, 50, 44,180, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 44, 44, 27, 27, 44, 44, 44, 44, 62, 36, - 155, 36, 36, 36, 36,181, 44, 44, 36, 36, 36, 43, 43, 80, 44, 44, - 36, 36, 36, 36, 36, 36, 36, 93, 36, 36, 44, 44, 36, 36, 36, 36, - 182,105,105, 44, 44, 44, 44, 44, 11, 11, 11, 11, 16, 16, 16, 16, - 11, 11, 44, 44, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 44, 44, - 36, 36, 36, 36, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, 44, 93, - 11, 11, 11, 11, 11, 47, 11, 11, 11, 47, 11,150, 16, 16, 16, 16, - 16,150, 16, 16, 16, 16, 16, 16, 16,150, 16, 16, 16,150,110, 44, - 40, 40, 40, 52, 40, 40, 40, 40, 81, 40, 40, 40, 40, 81, 44, 44, - 36, 36, 36, 44, 61, 36, 36, 36, 36, 36, 36, 62, 61, 44, 61, 62, - 36, 36, 36, 93, 27, 27, 27, 27, 36, 36, 36, 77,163, 27, 27, 27, - 44, 44, 44,179, 27, 27, 27, 27, 36, 61, 36, 44, 44,179, 27, 27, - 36, 36, 36, 27, 27, 27, 44, 93, 36, 36, 36, 36, 36, 44, 44, 93, - 36, 36, 36, 36, 44, 44, 27, 36, 44, 27, 27, 27, 27, 27, 27, 27, - 70, 43, 57, 80, 44, 44, 43, 43, 36, 36, 62, 36, 62, 36, 36, 36, - 36, 36, 36, 44, 43, 80, 44, 57, 27, 27, 27, 27,100, 44, 44, 44, - 2, 2, 2, 2, 64, 44, 44, 44, 36, 36, 36, 36, 36, 36,183, 30, - 36, 36, 36, 36, 36, 36,183, 27, 36, 36, 36, 36, 78, 36, 36, 36, - 36, 36, 70, 80, 44,179, 27, 27, 2, 2, 2, 64, 44, 44, 44, 44, - 36, 36, 36, 44, 93, 2, 2, 2, 36, 36, 36, 44, 27, 27, 27, 27, - 36, 61, 44, 44, 27, 27, 27, 27, 36, 44, 44, 44, 93, 2, 64, 44, - 44, 44, 44, 44,179, 27, 27, 27, 11, 47, 44, 44, 44, 44, 44, 44, - 16,110, 44, 44, 44, 27, 27, 27, 36, 36, 43, 43, 44, 44, 44, 44, - 7, 7, 7, 7, 7, 36, 36, 69, 11, 11, 11, 44, 57, 43, 43,159, - 16, 16, 16, 44, 44, 44, 44, 8, 27, 27, 27, 27, 27, 27, 27,100, - 36, 36, 36, 36, 36, 57,184, 44, 36, 44, 44, 44, 44, 44, 44, 44, - 44, 36, 61, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, - 27, 27, 27, 95, 44, 44, 44, 44,180, 27, 30, 2, 2, 44, 44, 44, - 36, 43, 43, 2, 2, 44, 44, 44, 36, 36,183, 27, 27, 27, 44, 44, - 87, 98, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, - 43, 43, 43, 60, 2, 2, 2, 44, 27, 27, 27, 7, 7, 7, 7, 7, - 71, 70, 71, 44, 44, 44, 44, 57, 86, 87, 43, 85, 87, 60,185, 2, - 2, 80, 44, 44, 44, 44, 79, 44, 43, 71, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 70, 43, 43, 87, 43, 43, 43, 80, 7, 7, 7, 7, 7, - 2, 2, 94, 98, 44, 44, 44, 44, 36, 70, 2, 61, 44, 44, 44, 44, - 36, 94, 86, 43, 43, 43, 43, 85, 98, 36, 63, 2, 59, 43, 60, 87, - 7, 7, 7, 7, 7, 63, 63, 2,179, 27, 27, 27, 27, 27, 27, 27, - 27, 27,100, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 86, 87, - 43, 86, 85, 43, 2, 2, 2, 71, 70, 44, 44, 44, 44, 44, 44, 44, - 36, 36, 36, 61, 61, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 62, - 36, 36, 36, 36, 63, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 70, - 86, 87, 43, 43, 43, 80, 44, 44, 43, 86, 62, 36, 36, 36, 61, 62, - 61, 36, 62, 36, 36, 57, 71, 86, 85, 86, 90, 89, 90, 89, 86, 44, - 61, 44, 44, 89, 44, 44, 62, 36, 36, 86, 44, 43, 43, 43, 80, 44, - 43, 43, 80, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 62, 44, 61, - 36, 36, 36, 62, 86, 87, 43, 43, 80, 90, 89, 89, 86, 90, 86, 85, - 71, 71, 2, 93, 64, 44, 44, 44, 57, 80, 44, 44, 44, 44, 44, 44, - 36, 36, 94, 86, 43, 43, 43, 43, 86, 43, 85, 71, 36, 63, 2, 2, - 7, 7, 7, 7, 7, 2, 93, 71, 86, 87, 43, 43, 85, 85, 86, 87, - 85, 43, 36, 72, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 94, - 86, 43, 43, 44, 86, 86, 43, 87, 60, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 36, 36, 43, 44, 86, 87, 43, 43, 43, 85, 87, 87, - 60, 2, 61, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 2, 64, 44, - 36, 36, 36, 36, 36, 70, 87, 86, 43, 43, 43, 87, 63, 44, 44, 44, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 44, 44, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 61, 57, 87, 86, 43, 43, 87, 43, 43, 44, 44, - 7, 7, 7, 7, 7, 27, 2, 97, 43, 43, 43, 43, 87, 60, 44, 44, - 27,100, 44, 44, 44, 44, 44, 62, 36, 36, 36, 61, 62, 44, 36, 36, - 36, 36, 62, 61, 36, 36, 36, 36, 86, 86, 86, 89, 90, 57, 85, 71, - 98, 87, 2, 64, 44, 44, 44, 44, 36, 36, 36, 36, 44, 36, 36, 36, - 94, 86, 43, 43, 44, 43, 86, 86, 71, 72, 90, 44, 44, 44, 44, 44, - 70, 43, 43, 43, 43, 71, 36, 36, 36, 70, 43, 43, 85, 70, 43, 60, - 2, 2, 2, 59, 44, 44, 44, 44, 70, 43, 43, 85, 87, 43, 36, 36, - 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 85, 43, 2, 72, 2, - 2, 64, 44, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 44, 44, 44, - 63, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 80, 43, 43, 43, 87, - 63, 2, 2, 44, 44, 44, 44, 44, 2, 36, 36, 36, 36, 36, 36, 36, - 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 89, 43, 43, 43, - 85, 43, 87, 80, 44, 44, 44, 44, 36, 36, 36, 61, 36, 62, 36, 36, - 70, 43, 43, 80, 44, 80, 43, 57, 43, 43, 43, 70, 44, 44, 44, 44, - 36, 36, 36, 62, 61, 36, 36, 36, 36, 36, 36, 36, 36, 86, 86, 90, - 43, 89, 87, 87, 61, 44, 44, 44, 36, 70, 85,107, 64, 44, 44, 44, - 43, 94, 36, 36, 36, 36, 36, 36, 36, 36, 86, 43, 43, 80, 44, 86, - 85, 60, 2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 7, 80, 44, 44, - 27, 27, 91, 67, 67, 67, 56, 20,168, 67, 67, 67, 67, 67, 67, 67, - 67, 44, 44, 44, 44, 44, 44, 93,105,105,105,105,105,105,105,181, - 2, 2, 64, 44, 44, 44, 44, 44, 63, 64, 44, 44, 44, 44, 44, 44, - 65, 65, 65, 65, 65, 65, 65, 65, 71, 36, 36, 70, 43, 43, 43, 43, - 43, 43, 43, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 43, - 43, 43, 43, 43, 43, 86, 87, 43, 43, 43, 60, 44, 44, 44, 44, 44, - 43, 43, 43, 60, 2, 2, 67, 67, 40, 40, 97, 44, 44, 44, 44, 44, - 7, 7, 7, 7, 7,179, 27, 27, 27, 62, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 44, 44, 62, 36, 40, 69, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 83,164, 2, 27, 27, 27, 30, 2, 64, 44, 44, - 36, 36, 36, 36, 36, 61, 44, 57, 94, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 44, 44, 44, 57, - 43, 74, 40, 40, 40, 40, 40, 40, 40, 88, 80, 44, 44, 44, 44, 44, - 86, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 62, - 40, 40, 52, 40, 40, 40, 52, 81, 36, 61, 44, 44, 44, 44, 44, 44, - 44, 61, 44, 44, 44, 44, 44, 44, 36, 61, 62, 44, 44, 44, 44, 44, - 44, 44, 36, 36, 44, 44, 44, 44, 36, 36, 36, 36, 36, 44, 50, 60, - 65, 65, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 44, - 43, 43, 43, 80, 44, 44, 44, 44, 67, 67, 67, 92, 55, 67, 67, 67, - 67, 67,186, 87, 43, 67,186, 86, 86,187, 65, 65, 65, 84, 43, 43, - 43, 76, 50, 43, 43, 43, 67, 67, 67, 67, 67, 67, 67, 43, 43, 67, - 67, 43, 76, 44, 44, 44, 44, 44, 27, 27, 44, 44, 44, 44, 44, 44, - 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 16, 16, 16,110, 16, 16, 16, 16, 16, - 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 47, 11, - 44, 47, 48, 47, 48, 11, 47, 11, 11, 11, 11, 16, 16,150,150, 16, - 16, 16,150, 16, 16, 16, 16, 16, 16, 16, 11, 48, 11, 47, 48, 11, - 11, 11, 47, 11, 11, 11, 47, 16, 16, 16, 16, 16, 11, 48, 11, 47, - 11, 11, 47, 47, 44, 11, 11, 11, 47, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, - 16, 16, 16, 44, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, - 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, - 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, - 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, - 16, 33, 16, 16, 16, 32, 44, 7, 43, 43, 43, 76, 67, 50, 43, 43, - 43, 43, 43, 43, 43, 43, 76, 67, 67, 67, 50, 67, 67, 67, 67, 67, - 67, 67, 76, 21, 2, 2, 44, 44, 44, 44, 44, 44, 44, 57, 43, 43, - 16, 16, 16, 16, 16, 39, 16, 16, 16, 16, 16, 16, 16, 16, 16,110, - 44, 44,150, 16, 16,110, 44, 44, 43, 43, 43, 80, 43, 43, 43, 43, - 43, 43, 43, 43, 80, 57, 43, 43, 43, 57, 80, 43, 43, 80, 44, 44, - 40, 40, 40, 40, 40, 40, 40, 44, 44, 44, 44, 44, 44, 44, 44, 57, - 43, 43, 43, 74, 40, 40, 40, 44, 7, 7, 7, 7, 7, 44, 44, 77, - 36, 36, 36, 36, 36, 36, 36, 80, 36, 36, 36, 36, 36, 36, 43, 43, - 7, 7, 7, 7, 7, 44, 44, 96, 36, 36, 36, 36, 36, 83, 43, 43, - 188, 7, 7, 7, 7,189, 44, 93, 36, 36, 36, 61, 36, 36, 62, 61, - 36, 36, 61,179, 27, 27, 27, 27, 16, 16, 43, 43, 43, 74, 44, 44, - 27, 27, 27, 27, 27, 27,163, 27,190, 27,100, 44, 44, 44, 44, 44, - 27, 27, 27, 27, 27, 27, 27,163, 27, 27, 27, 27, 27, 27, 27, 44, - 36, 36, 62, 36, 36, 36, 36, 36, 62, 61, 61, 62, 62, 36, 36, 36, - 36, 61, 36, 36, 62, 62, 44, 44, 44, 61, 44, 62, 62, 62, 62, 36, - 62, 61, 61, 62, 62, 62, 62, 62, 62, 61, 61, 62, 36, 61, 36, 36, - 36, 61, 36, 36, 62, 36, 61, 61, 36, 36, 36, 36, 36, 62, 36, 36, - 62, 36, 62, 36, 36, 62, 36, 36, 8, 44, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 67, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, - 27, 27, 27, 27, 27, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 44, - 44, 44, 44, 67, 67, 67, 67, 67, 67, 92, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 92, 44, 44, 44, 67, 44, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 25, 41, 41, 67, 67, 67, 67, 44, 44, 67, 67, - 67, 67, 67, 92, 44, 55, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 44, 44, 55, 67, 67, 67, 92, 44, 44, 44, 67, - 67, 67, 67, 67, 67, 67, 92, 55, 67, 92, 67, 67, 67, 67, 67, 67, - 79, 44, 44, 44, 44, 44, 44, 44,171,171,171,171,171,171,171, 44, - 171,171,171,171,171,171,171, 0, 0, 0, 29, 21, 21, 21, 23, 21, - 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, - 9, 22, 21, 18, 24, 16, 24, 5, 5, 5, 5, 22, 25, 18, 25, 0, - 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, 26, 25, 15, 15, - 24, 15, 7, 19, 15, 21, 9, 25, 9, 5, 5, 25, 5, 9, 5, 7, - 7, 7, 9, 8, 8, 5, 7, 5, 6, 6, 24, 24, 6, 24, 12, 12, - 2, 2, 6, 5, 9, 21, 9, 2, 2, 9, 25, 9, 26, 12, 11, 11, - 2, 6, 5, 21, 17, 2, 2, 26, 26, 23, 2, 12, 17, 12, 21, 12, - 12, 21, 7, 2, 2, 7, 7, 21, 21, 2, 1, 1, 21, 23, 26, 26, - 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, 12, 6, 6, 12, - 12, 26, 7, 26, 26, 7, 2, 1, 12, 2, 6, 2, 24, 7, 7, 6, - 1, 12, 12, 10, 10, 10, 10, 12, 21, 6, 2, 10, 10, 2, 15, 26, - 26, 2, 2, 21, 7, 10, 15, 7, 2, 23, 21, 26, 10, 7, 21, 15, - 15, 2, 17, 7, 29, 7, 7, 22, 18, 2, 14, 14, 14, 7, 10, 21, - 17, 21, 11, 12, 5, 2, 5, 6, 8, 8, 8, 24, 5, 24, 2, 24, - 9, 24, 24, 2, 29, 29, 29, 1, 17, 17, 20, 19, 22, 20, 27, 28, - 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, 25, 22, 18, 21, 21, 29, - 1, 2, 15, 6, 18, 6, 23, 2, 12, 11, 9, 26, 26, 9, 26, 5, - 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, 26, 22, 18, 26, 18, 25, - 18, 22, 5, 12, 2, 5, 22, 21, 21, 22, 18, 17, 26, 6, 7, 14, - 17, 22, 18, 18, 26, 14, 17, 6, 14, 6, 12, 24, 24, 6, 26, 15, - 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, 6, 10, 4, 4, 3, 3, - 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, 25, 2, 25, 24, 2, 15, - 12, 15, 14, 2, 21, 14, 7, 15, 12, 17, 21, 1, 26, 10, 10, 1, - 7, 13, 13, 2, 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 0, 10, 11, 12, 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, + 32, 32, 11, 11, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 46, 43, + 51, 39,166, 35, 39, 35, 36, 36, 36, 70, 36, 70, 36, 69, 36, 36, + 36, 93, 86, 84, 66, 66, 79, 43, 27, 27, 27, 66,167, 43, 43, 43, + 36, 36, 2, 2, 43, 43, 43, 43, 85, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 85, 85, 85, 85, 85, 85, 85, 85, 42, 43, 43, 43, 43, 2, + 42, 36, 36, 36, 2, 71, 71, 69, 36, 36, 36, 42, 42, 42, 42, 2, + 36, 36, 36, 69, 42, 42, 42, 42, 42, 85, 43, 43, 43, 43, 43, 92, + 36, 69, 85, 42, 42, 85, 42, 85,106, 2, 2, 2, 2, 2, 2, 51, + 7, 7, 7, 7, 7, 43, 43, 2, 36, 36, 69, 68, 36, 36, 36, 36, + 7, 7, 7, 7, 7, 36, 36, 60, 36, 36, 36, 36, 69, 42, 42, 84, + 86, 84, 86, 79, 43, 43, 43, 43, 36, 69, 36, 36, 36, 36, 84, 43, + 7, 7, 7, 7, 7, 43, 2, 2, 68, 36, 36, 76, 66, 93, 84, 36, + 70, 42, 70, 69, 70, 36, 36, 42, 69, 60, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 61, 82, 2, 36, 36, 36, 36, 36, 93, 42, 85, + 2, 82,168, 79, 43, 43, 43, 43, 61, 36, 36, 60, 61, 36, 36, 60, + 61, 36, 36, 60, 43, 43, 43, 43, 16, 16, 16, 16, 16,113, 39, 39, + 16, 16, 16, 16,110, 40, 43, 43, 36, 93, 86, 85, 84,106, 86, 43, + 36, 36, 43, 43, 43, 43, 43, 43, 36, 36, 36, 60, 43, 61, 36, 36, + 169,169,169,169,169,169,169,169,170,170,170,170,170,170,170,170, + 16, 16, 16,109, 43, 43, 43, 43, 43,149, 16, 16, 43, 43, 61, 70, + 36, 36, 36, 36,171, 36, 36, 36, 36, 36, 36, 60, 36, 36, 60, 60, + 36, 61, 60, 36, 36, 36, 36, 36, 36, 40, 40, 40, 40, 40, 40, 40, + 40, 22, 66, 66, 66, 66, 66, 66, 66, 77, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36,147, 66, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 66, 66, 66, 66, 36, 36, 36, 36, 36, 36,167, 66, + 2, 2, 2,151,129, 43, 43, 43, 6,172,173,147,147,147,147,147, + 147,147,129,151,129, 2,126,174, 2, 63, 2, 2,155,147,147,129, + 2,175, 8,176, 65, 2, 43, 43, 36, 36, 60, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 60, 78, 92, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18,128,129, 4, 2, 36, 36, 36, 36, 36, + 68, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 39, + 43, 36, 36, 36, 43, 36, 36, 36, 43, 36, 36, 36, 43, 36, 60, 43, + 20,177, 55,178, 26, 8,143, 91, 43, 43, 43, 43, 78, 64, 66, 43, + 36, 36, 36, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 60, 36, 61, + 2, 63, 43,179, 27, 27, 27, 27, 27, 27, 43, 54, 66, 66, 66, 66, + 104,104,142, 27, 90, 66, 66, 66, 66, 66, 66, 66, 66, 27, 66, 91, + 66, 66, 66, 66, 66, 66, 91, 43, 91, 43, 43, 43, 43, 43, 43, 43, + 66, 66, 66, 66, 66, 66, 49, 43,180, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 43, 43, 27, 27, 43, 43, 43, 43, 61, 36, + 154, 36, 36, 36, 36,181, 43, 43, 36, 36, 36, 42, 42, 79, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 92, 36, 36, 43, 43, 36, 36, 36, 36, + 182,104,104, 43, 43, 43, 43, 43, 11, 11, 11, 11, 16, 16, 16, 16, + 11, 11, 43, 43, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 43, 43, + 36, 36, 36, 36, 43, 43, 43, 43, 36, 36, 43, 43, 43, 43, 43, 92, + 11, 11, 11, 11, 11, 46, 11, 11, 11, 46, 11,149, 16, 16, 16, 16, + 16,149, 16, 16, 16, 16, 16, 16, 16,149, 16, 16, 16,149,109, 43, + 39, 39, 39, 51, 39, 39, 39, 39, 80, 39, 39, 39, 39, 80, 43, 43, + 36, 36, 36, 43, 60, 36, 36, 36, 36, 36, 36, 61, 60, 43, 60, 61, + 36, 36, 36, 92, 27, 27, 27, 27, 36, 36, 36, 76,162, 27, 27, 27, + 43, 43, 43,179, 27, 27, 27, 27, 36, 60, 36, 43, 43,179, 27, 27, + 36, 36, 36, 27, 27, 27, 43, 92, 36, 36, 36, 36, 36, 43, 43, 92, + 36, 36, 36, 36, 43, 43, 27, 36, 43, 27, 27, 27, 27, 27, 27, 27, + 69, 42, 56, 79, 43, 43, 42, 42, 36, 36, 61, 36, 61, 36, 36, 36, + 36, 36, 36, 43, 42, 79, 43, 56, 27, 27, 27, 27, 99, 43, 43, 43, + 2, 2, 2, 2, 63, 43, 43, 43, 36, 36, 36, 36, 36, 36,183, 30, + 36, 36, 36, 36, 36, 36,183, 27, 36, 36, 36, 36, 77, 36, 36, 36, + 36, 36, 69, 79, 43,179, 27, 27, 2, 2, 2, 63, 43, 43, 43, 43, + 36, 36, 36, 43, 92, 2, 2, 2, 36, 36, 36, 43, 27, 27, 27, 27, + 36, 60, 43, 43, 27, 27, 27, 27, 36, 43, 43, 43, 92, 2, 63, 43, + 43, 43, 43, 43,179, 27, 27, 27, 11, 46, 43, 43, 43, 43, 43, 43, + 16,109, 43, 43, 43, 27, 27, 27, 36, 36, 42, 42, 43, 43, 43, 43, + 7, 7, 7, 7, 7, 36, 36, 68, 11, 11, 11, 43, 56, 42, 42,158, + 16, 16, 16, 43, 43, 43, 43, 8, 27, 27, 27, 27, 27, 27, 27, 99, + 36, 36, 36, 36, 36, 56,184, 43, 36, 43, 43, 43, 43, 43, 43, 43, + 43, 36, 82, 36, 43, 43, 43, 43, 96, 66, 66, 66, 91, 43, 43, 43, + 43, 43, 43, 43, 43, 42, 42, 42, 27, 27, 27, 94, 43, 43, 43, 43, + 180, 27, 30, 2, 2, 43, 43, 43, 36, 42, 42, 2, 2, 43, 43, 43, + 36, 36,183, 27, 27, 27, 43, 43, 86, 97, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 42, 59, 2, 2, 2, 43, + 27, 27, 27, 7, 7, 7, 7, 7, 70, 69, 70, 43, 43, 43, 43, 56, + 85, 86, 42, 84, 86, 59,185, 2, 2, 79, 43, 43, 43, 43, 78, 43, + 42, 70, 36, 36, 36, 36, 36, 36, 36, 36, 36, 69, 42, 42, 86, 42, + 42, 42, 79, 7, 7, 7, 7, 7, 2, 2, 93, 97, 43, 43, 43, 43, + 36, 69, 2, 60, 43, 43, 43, 43, 36, 93, 85, 42, 42, 42, 42, 84, + 97, 36, 62, 2, 58, 42, 59, 86, 7, 7, 7, 7, 7, 62, 62, 2, + 179, 27, 27, 27, 27, 27, 27, 27, 27, 27, 99, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 85, 86, 42, 85, 84, 42, 2, 2, 2, 70, + 69, 43, 43, 43, 43, 43, 43, 43, 36, 36, 36, 60, 60, 36, 36, 61, + 36, 36, 36, 36, 36, 36, 36, 61, 36, 36, 36, 36, 62, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 69, 85, 86, 42, 42, 42, 79, 43, 43, + 42, 85, 61, 36, 36, 36, 60, 61, 60, 36, 61, 36, 36, 56, 70, 85, + 84, 85, 89, 88, 89, 88, 85, 43, 60, 43, 43, 88, 43, 43, 61, 36, + 36, 85, 43, 42, 42, 42, 79, 43, 42, 42, 79, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 61, 43, 60, 36, 36, 36, 61, 85, 86, 42, 42, + 79, 89, 88, 88, 85, 89, 85, 84, 70, 70, 2, 92, 63, 43, 43, 43, + 56, 79, 43, 43, 43, 43, 43, 43, 36, 36, 93, 85, 42, 42, 42, 42, + 85, 42, 84, 70, 36, 62, 2, 2, 7, 7, 7, 7, 7, 2, 92, 70, + 85, 86, 42, 42, 84, 84, 85, 86, 84, 42, 36, 71, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 93, 85, 42, 42, 43, 85, 85, 42, 86, + 59, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 42, 43, + 85, 86, 42, 42, 42, 84, 86, 86, 59, 2, 60, 43, 43, 43, 43, 43, + 2, 2, 2, 2, 2, 2, 63, 43, 36, 36, 36, 36, 36, 69, 86, 85, + 42, 42, 42, 86, 62, 43, 43, 43, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 60, 56, 86, + 85, 42, 42, 86, 42, 42, 43, 43, 7, 7, 7, 7, 7, 27, 2, 96, + 42, 42, 42, 42, 86, 59, 43, 43, 27, 99, 43, 43, 43, 43, 43, 61, + 36, 36, 36, 60, 61, 43, 36, 36, 36, 36, 61, 60, 36, 36, 36, 36, + 85, 85, 85, 88, 89, 56, 84, 70, 97, 86, 2, 63, 43, 43, 43, 43, + 36, 36, 36, 36, 43, 36, 36, 36, 93, 85, 42, 42, 43, 42, 85, 85, + 70, 71, 89, 43, 43, 43, 43, 43, 69, 42, 42, 42, 42, 70, 36, 36, + 36, 69, 42, 42, 84, 69, 42, 59, 2, 2, 2, 58, 43, 43, 43, 43, + 69, 42, 42, 84, 86, 42, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, + 42, 42, 42, 84, 42, 2, 71, 2, 2, 63, 43, 43, 43, 43, 43, 43, + 2, 2, 2, 2, 2, 43, 43, 43, 84, 42, 84, 84, 43, 43, 43, 43, + 62, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 79, 42, 42, 42, 86, + 62, 2, 2, 43, 43, 43, 43, 43, 2, 36, 36, 36, 36, 36, 36, 36, + 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 88, 42, 42, 42, + 84, 42, 86, 79, 43, 43, 43, 43, 36, 36, 36, 60, 36, 61, 36, 36, + 69, 42, 42, 79, 43, 79, 42, 56, 42, 42, 42, 69, 43, 43, 43, 43, + 36, 36, 36, 61, 60, 36, 36, 36, 36, 36, 36, 36, 36, 85, 85, 89, + 42, 88, 86, 86, 60, 43, 43, 43, 36, 36, 36, 36, 82, 36, 43, 43, + 36, 69, 84,106, 63, 43, 43, 43, 42, 93, 36, 36, 36, 36, 36, 36, + 36, 36, 85, 42, 42, 79, 43, 85, 84, 59, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 79, 43, 43, 27, 27, 90, 66, 66, 66, 55, 20, + 167, 66, 66, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, 43, 43, 92, + 104,104,104,104,104,104,104,181, 2, 2, 63, 43, 43, 43, 43, 43, + 62, 63, 43, 43, 43, 43, 43, 43, 64, 64, 64, 64, 64, 64, 64, 64, + 70, 36, 36, 69, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 85, 86, 42, + 42, 42, 59, 43, 43, 43, 43, 43, 42, 42, 42, 59, 2, 2, 66, 66, + 39, 39, 96, 43, 43, 43, 43, 43, 7, 7, 7, 7, 7,179, 27, 27, + 27, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 61, 36, + 39, 68, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 82,163, 2, + 27, 27, 27, 30, 2, 63, 43, 43, 11, 11, 11, 11, 46,149, 16, 16, + 16, 16, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 60, 43, 56, + 93, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 43, 43, 43, 56, 42, 73, 39, 39, 39, 39, 39, 39, + 39, 87, 79, 43, 43, 43, 43, 43, 85, 39,104,181, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 61, 36, 60, 43, 43, 43, 43, 43, 43, + 39, 39, 51, 39, 39, 39, 51, 80, 43, 60, 43, 43, 43, 43, 43, 43, + 36, 60, 61, 43, 43, 43, 43, 43, 43, 43, 36, 36, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 43, 49, 59, 64, 64, 43, 43, 43, 43, 43, 43, + 7, 7, 7, 7, 7, 66, 91, 43, 66, 66, 43, 43, 43, 66, 66, 66, + 176, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 79, 43, 43, 43, 43, + 66, 66, 66, 91, 54, 66, 66, 66, 66, 66,186, 86, 42, 66,186, 85, + 85,187, 64, 64, 64, 83, 42, 42, 42, 75, 49, 42, 42, 42, 66, 66, + 66, 66, 66, 66, 66, 42, 42, 66, 66, 42, 75, 43, 43, 43, 43, 43, + 27, 27, 43, 43, 43, 43, 43, 43, 11, 11, 11, 11, 11, 16, 16, 16, + 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, + 16, 16,109, 16, 16, 16, 16, 16, 11, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 46, 11, 43, 46, 47, 46, 47, 11, 46, 11, + 11, 11, 11, 16, 16,149,149, 16, 16, 16,149, 16, 16, 16, 16, 16, + 16, 16, 11, 47, 11, 46, 47, 11, 11, 11, 46, 11, 11, 11, 46, 16, + 16, 16, 16, 16, 11, 47, 11, 46, 11, 11, 46, 46, 43, 11, 11, 11, + 46, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, + 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 43, 11, 11, 11, 11, + 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, + 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, + 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, + 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 43, 7, + 42, 42, 42, 75, 66, 49, 42, 42, 42, 42, 42, 42, 42, 42, 75, 66, + 66, 66, 49, 66, 66, 66, 66, 66, 66, 66, 75, 21, 2, 2, 43, 43, + 43, 43, 43, 43, 43, 56, 42, 42, 16, 16, 16, 16, 16,138, 16, 16, + 16, 16, 16, 16, 16, 16, 16,109, 43, 43,149, 16, 16,109, 43, 43, + 42, 42, 42, 79, 42, 42, 42, 42, 42, 42, 42, 42, 79, 56, 42, 42, + 42, 56, 79, 42, 42, 79, 43, 43, 39, 39, 39, 39, 39, 39, 39, 43, + 43, 43, 43, 43, 43, 43, 43, 56, 42, 42, 42, 73, 39, 39, 39, 43, + 7, 7, 7, 7, 7, 43, 43, 76, 36, 36, 36, 36, 36, 36, 36, 79, + 36, 36, 36, 36, 36, 36, 42, 42, 7, 7, 7, 7, 7, 43, 43, 95, + 36, 36, 36, 36, 36, 82, 42, 42,188, 7, 7, 7, 7,189, 43, 92, + 36, 69, 36, 70, 36, 36, 36, 42, 36, 36, 69, 43, 43, 43, 43, 82, + 36, 36, 36, 60, 36, 36, 61, 60, 36, 36, 60,179, 27, 27, 27, 27, + 16, 16, 42, 42, 42, 73, 43, 43, 27, 27, 27, 27, 27, 27,162, 27, + 190, 27, 99, 43, 43, 43, 43, 43, 27, 27, 27, 27, 27, 27, 27,162, + 27, 27, 27, 27, 27, 27, 27, 43, 36, 36, 61, 36, 36, 36, 36, 36, + 61, 60, 60, 61, 61, 36, 36, 36, 36, 60, 36, 36, 61, 61, 43, 43, + 43, 60, 43, 61, 61, 61, 61, 36, 61, 60, 60, 61, 61, 61, 61, 61, + 61, 60, 60, 61, 36, 60, 36, 36, 36, 60, 36, 36, 61, 36, 60, 60, + 36, 36, 36, 36, 36, 61, 36, 36, 61, 36, 61, 36, 36, 61, 36, 36, + 8, 43, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 66, 66, 43, 43, + 54, 66, 66, 66, 66, 66, 66, 66, 27, 27, 27, 27, 27, 27, 90, 66, + 66, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, 66, 66, 66, 66, 66, + 66, 91, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 91, 43, 43, 43, + 66, 43, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 66, 25, 40, 40, + 66, 66, 66, 66, 91, 43, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, + 8, 8, 8, 8,176, 43, 43, 43, 66, 66, 66, 66, 66, 91, 43, 66, + 66, 66, 66, 91, 91, 43, 54, 66, 66, 66, 66, 66, 66, 66, 91, 54, + 66, 66, 66, 66, 66, 91, 43, 54, 66, 91, 66, 66, 66, 66, 66, 66, + 7, 7, 7, 7, 7, 91, 43, 43, 78, 43, 43, 43, 43, 43, 43, 43, + 170,170,170,170,170,170,170, 43,170,170,170,170,170,170,170, 0, + 0, 0, 29, 21, 21, 21, 23, 21, 22, 18, 21, 25, 21, 17, 13, 13, + 25, 25, 25, 21, 21, 9, 9, 9, 9, 22, 21, 18, 24, 16, 24, 5, + 5, 5, 5, 22, 25, 18, 25, 0, 23, 23, 26, 21, 24, 26, 7, 20, + 25, 1, 26, 24, 26, 25, 15, 15, 24, 15, 7, 19, 15, 21, 9, 25, + 9, 5, 5, 25, 5, 9, 5, 7, 7, 7, 9, 8, 8, 5, 6, 6, + 24, 24, 6, 24, 12, 12, 2, 2, 6, 5, 9, 21, 9, 2, 2, 9, + 25, 9, 26, 12, 11, 11, 2, 6, 5, 21, 17, 2, 2, 26, 26, 23, + 2, 12, 17, 12, 21, 12, 12, 21, 7, 2, 2, 7, 7, 21, 21, 2, + 1, 1, 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, + 12, 1, 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, 2, 1, 12, 2, + 6, 2, 24, 7, 7, 6, 1, 12, 12, 10, 10, 10, 10, 12, 21, 6, + 2, 10, 10, 2, 15, 26, 26, 2, 2, 21, 7, 10, 15, 7, 2, 23, + 21, 26, 10, 7, 21, 15, 15, 2, 17, 7, 29, 7, 7, 22, 18, 2, + 14, 14, 14, 7, 10, 21, 17, 21, 11, 12, 5, 2, 5, 6, 8, 8, + 8, 24, 5, 24, 2, 24, 9, 24, 24, 2, 29, 29, 29, 1, 17, 17, + 20, 19, 22, 20, 27, 28, 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, + 25, 22, 18, 21, 21, 29, 1, 2, 15, 6, 18, 6, 12, 11, 9, 26, + 26, 9, 26, 5, 7, 5, 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, + 26, 22, 18, 26, 18, 25, 18, 22, 5, 12, 2, 5, 22, 21, 21, 22, + 18, 17, 26, 6, 7, 14, 17, 22, 18, 18, 26, 14, 17, 6, 14, 6, + 12, 24, 24, 6, 26, 15, 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, + 6, 10, 4, 4, 3, 3, 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, + 25, 2, 25, 24, 23, 2, 2, 15, 12, 15, 14, 2, 21, 14, 7, 15, + 12, 17, 21, 1, 26, 10, 10, 1, 7, 13, 13, 2, 23, 15, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, 14, 0, + 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, + 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 21, 22, 23, + 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, + 0, 0, 0, 36, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 36, 0, 37, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, + 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, + 6, 7, 8, 0, 9, 0, 10, 11, 0, 0, 12, 13, 14, 15, 16, 0, + 0, 0, 0, 17, 18, 19, 20, 0, 21, 0, 22, 23, 0, 24, 25, 0, + 0, 24, 26, 27, 0, 24, 26, 0, 0, 24, 26, 0, 0, 24, 26, 0, + 0, 0, 26, 0, 0, 24, 28, 0, 0, 24, 26, 0, 0, 29, 26, 0, + 0, 0, 30, 0, 0, 31, 32, 0, 0, 33, 34, 0, 35, 36, 0, 37, + 38, 0, 39, 0, 0, 40, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 0, 0, + 0, 0, 45, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 47, 0, 0, + 0, 0, 0, 0, 48, 0, 0, 49, 0, 50, 51, 52, 0, 53, 54, 55, + 0, 56, 0, 57, 0, 58, 0, 0, 0, 0, 59, 60, 0, 0, 0, 0, + 0, 0, 61, 62, 0, 0, 0, 0, 0, 0, 63, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 66, + 0, 0, 0, 67, 0, 68, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 70, 71, 0, 0, 72, 0, 0, 0, 0, + 0, 0, 0, 0, 73, 74, 0, 0, 0, 0, 54, 75, 0, 76, 77, 0, + 0, 78, 79, 0, 0, 0, 0, 0, 0, 80, 81, 82, 0, 0, 0, 0, + 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 83, 0, 0, 0, + 0, 0, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 85, 0, 0, 0, 0, 0, 0, 0, 86, 0, 0, 0, 87, + 0, 0, 0, 0, 88, 89, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 0, 92, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 0, 94, 0, 0, 95, 0, + 96, 0, 0, 0, 0, 0, 73, 97, 0, 98, 0, 0, 99,100, 0, 78, + 0, 0,101, 0, 0,102, 0, 0, 0, 0, 0,103, 0,104, 26,105, + 0, 0,106, 0, 0, 0,107, 0, 0, 0,108, 0, 0, 0, 0, 0, + 0, 66,109, 0, 0, 66, 0, 0, 0,110, 0, 0, 0,111, 0, 0, + 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 0, 0, 0,112,113, 0, + 0, 0, 0, 79, 0, 44,114, 0,115, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0,116, 0, + 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,118, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,119, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,120, 0,121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 40, - 0, 0, 0, 0, 0, 0, 41, 42, 43, 0, 44, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, - 0, 0, 4, 5, 6, 7, 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, - 16, 17, 16, 18, 16, 19, 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, - 21, 19, 0, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, - 34, 0, 0, 35, 0, 0, 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, - 0, 0, 0, 0, 0, 0, 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, - 0, 0, 0, 0, 0, 55, 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, - 60, 61, 62, 63, 0, 0, 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, - 67, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 69, 0, 0, 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, - 0, 0, 0, 0, 0, 0, 0, 0, 74, 75, 0, 0, 0, 0, 76, 77, - 0, 78, 79, 0, 0, 80, 81, 0, 82, 62, 0, 83, 84, 0, 0, 85, - 86, 87, 0, 88, 0, 89, 0, 90, 0, 0, 51, 91, 51, 0, 92, 0, - 93, 0, 0, 0, 81, 0, 0, 0, 94, 95, 0, 96, 97, 98, 99, 0, - 0, 0, 0, 0, 51, 0, 0, 0, 0,100,101, 0, 0, 0, 0, 0, - 0,102, 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0, 0, 0,104, - 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,106, 0, 0,107, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,108,109, 0, 0,110, 0, 0, - 0, 0, 0, 0,111, 0,112, 0,105, 0, 0, 0, 0, 0,113,114, - 0, 0, 0, 0, 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0,117, - 0,118, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, - 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, - 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, - 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 0, 0, - 28, 29, 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, 0, 35, - 33, 0, 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, - 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, - 0, 43, 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, 0, 0, - 0, 0, 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 52, - 0, 53, 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, - 0, 0, 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, 0, 0, 0, 61, - 52, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, 0, 0, 0, 67, - 0, 68, 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, 0, 0, 77, 78, - 0, 0, 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, 0, 81, 0, 0, - 0, 0, 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, 84, 0, 85, 0, - 52, 0, 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, 0, 0, 0, 88, - 57, 0, 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, 0, 0, 33, 0, - 0, 91, 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, 0, 0, 93, 0, - 0, 0, 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, 0, 0, 98, 0, - 0, 0, 99, 0, 0, 0,100, 0, 0, 0, 0,101,102, 93, 0, 0, - 103, 0, 0, 0, 84, 0, 0,104, 0, 0, 0,105,106, 0, 0,107, - 108, 0, 0, 0, 0, 0, 0,109, 0, 0,110, 0, 0, 0, 0,111, - 33, 0,112,113,114, 57, 0, 0,115, 35, 0, 0,116, 0, 0, 0, - 117, 0, 0, 0, 0, 0, 0,118, 0, 0,119, 0, 0, 0, 0,120, - 88, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 52,121, 0, 0, 0, - 0,122, 0, 0,123, 0, 0, 0, 0,121, 0, 0,124, 0, 0, 0, - 0, 0, 79, 0, 0, 0, 0,125, 0, 0, 0,126, 0, 0, 0,127, - 0,128, 0, 0, 0, 0,129,130,131, 0,132, 0,133, 0, 0, 0, - 134,135,136, 0, 77, 0, 0, 0, 0, 0, 35, 0, 0, 0,137, 0, - 0, 0,138, 0, 0, 0,139, 0, 0,140, 0, 0,141, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 4, - 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, 1, 1, - 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, - 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, 1, 36, - 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, 0, 0, - 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, 0, 47, - 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, 0, 19, 52, 1, - 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, 35, 1, - 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, 0, 59, - 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, 64, 0, - 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, 68, 0, - 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, 0, 0, - 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, 0, 0, - 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, 83, 0, - 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, 15, 86, - 36, 10, 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, - 0, 0, 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, - 87, 9, 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, 21, 1, 21, 92, - 93, 1, 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, 81, 99,100, 4, - 58, 0, 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, - 0, 0,101,102, 0, 0,103, 0, 0, 1, 1, 50, 0, 0, 0, 38, - 0, 63, 0, 0, 0, 0, 0, 62, 0, 0,104, 68, 61, 0, 0, 0, - 78, 0, 0, 0,105,106, 58, 38, 81, 0, 0, 0, 0, 0, 0,107, - 1, 14, 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, 0, 0, 0,108, - 0, 0,109, 61, 0,110, 0, 0, 0, 1, 0, 0, 0, 0, 49, 50, - 0, 0, 19, 58, 0, 0, 0, 51, 0,111, 14, 52,112, 41, 0, 0, - 62, 0, 0, 61, 0, 0,113, 0, 87, 0, 0, 0, 61, 62, 0, 0, - 62, 0, 89, 0, 0,113, 0, 0, 0, 0,114, 0, 0, 0, 78, 55, - 0, 38, 1, 58, 1, 58, 0, 0, 0, 0, 0, 88, 63, 89, 0, 0, - 115, 0, 0, 0, 55, 0, 0, 0, 0,115, 0, 0, 0, 0, 61, 0, - 0, 0, 0, 79, 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, - 79, 0, 0, 0, 8, 91, 0, 0, 1, 87, 0, 0,116, 0, 0, 0, - 0, 0, 0,117, 0,118,119,120,121, 0,104, 4,122, 49, 23, 0, - 0, 0, 38, 50, 38, 58, 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, - 48,105, 87, 0, 0, 0, 0, 1, 0, 0, 0,123, 0, 0, 0,112, - 4,122, 0, 0, 0, 1,124, 0, 0, 0, 0, 0,230,230,230,230, - 230,232,220,220,220,220,232,216,220,220,220,220,220,202,202,220, - 220,220,220,202,202,220,220,220, 1, 1, 1, 1, 1,220,220,220, - 220,230,230,230,230,240,230,220,220,220,230,230,230,220,220, 0, - 230,230,230,220,220,220,220,230,232,220,220,230,233,234,234,233, - 234,234,233,230, 0, 0, 0,230, 0,220,230,230,230,230,220,230, - 230,230,222,220,230,230,220,220,230,222,228,230, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, 25, 0, - 230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, 30, 31, - 32, 33, 34,230,230,220,220,230,220,230,230,220, 35, 0, 0, 0, - 0, 0,230,230,230, 0, 0,230,230, 0,220,230,230,220, 0, 0, - 0, 36, 0, 0,230,220,230,230,220,220,230,220,220,230,220,230, - 220,230,230, 0, 0,220, 0, 0,230,230, 0,230, 0,230,230,230, - 230,230, 0, 0, 0,220,220,220,230,220,220,220,230,230, 0,220, - 27, 28, 29,230, 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230, - 230, 0, 0, 0, 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, - 9, 0, 0, 0, 0, 0, 9, 0,103,103, 9, 0,107,107,107,107, - 118,118, 9, 0,122,122,122,122,220,220, 0, 0, 0,220, 0,220, - 0,216, 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130, - 130,130, 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, - 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, 0,230, 0, 0, - 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0,230, 0, 0,220, - 230,220, 0,220,230,230,230, 0, 0, 0, 9, 9, 0, 0, 7, 0, + 0, 0, 0,122, 0, 0, 0, 0,123, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,124, + 125,126, 0, 0, 0, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,128,129, 0, 0,130, 0, 0, 0, 0,121, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,131, 0,132, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,133, 0, 0, 0, 0, + 0, 0, 0,134, 0, 0, 0, 0, 0, 0, 0,135, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,136, 0, 0, 0,137, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 2, 3, 4, 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, + 14, 15, 16, 17, 18, 1, 1, 1, 0, 0, 0, 0, 19, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, + 25, 26, 27, 28, 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, 1, 36, 0, 0, 0, 0, + 37, 0, 0, 0, 0, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, + 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 0, 0, 0, 0, 19, 1, + 21, 0, 0, 47, 0, 0, 0, 0, 0, 38, 48, 1, 1, 49, 49, 50, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, + 0, 19, 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, + 54, 21, 35, 1, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 56, + 57, 58, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 59, 0, 0, 0, 56, 0, 60, 0, 0, 0, 0, 0, 0, + 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 68, 0, 0, 0, 0, 0, 0, 69, 70, 0, 0, 0, 0, 0, + 71, 72, 73, 74, 75, 76, 0, 0, 0, 0, 0, 0, 0, 77, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, + 0, 80, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, + 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 83, 0, 0, 0, 0, 0, 0, 19, 84, 0, 62, 0, 0, 0, + 0, 49, 1, 85, 0, 0, 0, 0, 1, 52, 15, 86, 36, 10, 21, 1, + 1, 1, 1, 41, 1, 21, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 55, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 19, 10, + 1, 0, 0, 0, 0, 0, 88, 0, 0, 0, 0, 0, 0, 89, 0, 0, + 88, 0, 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, + 90, 9, 12, 4, 91, 8, 92, 47, 0, 58, 50, 0, 21, 1, 21, 93, + 94, 1, 1, 1, 1, 1, 1, 1, 1, 95, 96, 97, 0, 0, 0, 0, + 98, 1, 99, 58, 81,100,101, 4, 58, 0, 0, 0, 0, 0, 0, 19, + 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 0,102,103, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,104, 0, 0, 0, 0, 19, 0, 1, 1, 50, 0, 0, 0, 0, + 0, 0, 0, 38, 0, 0, 0, 0, 50, 0, 0, 0, 0, 63, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 1, 1, 1, 1, + 50, 0, 0, 0, 0, 0,105, 68, 0, 0, 0, 0, 0, 0, 0, 0, + 61, 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 62, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,106,107, 58, 38, 81, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, + 0, 0, 0,108, 1, 14, 4, 12, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 47, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 38, 90, 0, 0, 0, 0,109, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,110, 61, 0,111, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 49, 50, 0, 0, 0, 0, 0, 0, 19, 58, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,112, 51, 0,112, 14, 52, + 84, 0, 0, 0,113, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 62, 0, 0, 61, 0, 0, 0, 0, 0, 0,114, 0, 90, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,114, 0, 0, 0, 0,115, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 55, 0, 38, 1, 58, + 1, 58, 0, 0, 0, 0, 0, 88, 62, 0, 0, 0, 63, 89, 0, 0, + 0, 0, 0, 59,116, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,116, 0, 0, 0, 0, 61, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 78, 0, 0, 0, + 0, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 56, 0, 89, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 61, 0, 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 92, 0, 0, 0, 0, 0, 0, + 1, 90, 0, 0, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,118, 0,119,120,121,122, 0,105, 4,123, 49, 23, 0, + 0, 0, 0, 0, 0, 0, 38, 50, 0, 0, 0, 0, 38, 58, 0, 0, + 0, 0, 0, 0, 1, 90, 1, 1, 1, 1, 39, 1, 48,106, 90, 0, + 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 59, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,124, + 0, 0, 0, 0, 0, 0, 0,113, 0, 0, 0, 0, 19, 59, 0, 38, + 0, 81, 0, 0, 0, 0, 0, 0, 4,123, 0, 0, 0, 1,125, 0, + 0, 0, 0, 0, 0, 0, 0, 0,230,230,230,230,230,232,220,220, + 220,220,232,216,220,220,220,220,220,202,202,220,220,220,220,202, + 202,220,220,220, 1, 1, 1, 1, 1,220,220,220,220,230,230,230, + 230,240,230,220,220,220,230,230,230,220,220, 0,230,230,230,220, + 220,220,220,230,232,220,220,230,233,234,234,233,234,234,233,230, + 0, 0, 0,230, 0,220,230,230,230,230,220,230,230,230,222,220, + 230,230,220,220,230,222,228,230, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, 25, 0,230,220, 0, 18, + 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230, + 230,220,220,230,220,230,230,220, 35, 0, 0, 0, 0, 0,230,230, + 230, 0, 0,230,230, 0,220,230,230,220, 0, 0, 0, 36, 0, 0, + 230,220,230,230,220,220,230,220,220,230,220,230,220,230,230, 0, + 0,220, 0, 0,230,230, 0,230, 0,230,230,230,230,230, 0, 0, + 0,220,220,220,230,220,220,220,230,230, 0,220, 27, 28, 29,230, + 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, + 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, + 0, 0, 9, 0,103,103, 9, 0,107,107,107,107,118,118, 9, 0, + 122,122,122,122,220,220, 0, 0, 0,220, 0,220, 0,216, 0, 0, + 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130,130,130, 0, 0, + 130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, 0, 0, 0, 7, + 0, 9, 9, 0, 9, 9, 0, 0, 0,230, 0, 0, 0,228, 0, 0, + 0,222,230,220,220, 0, 0, 0,230, 0, 0,220,230,220, 0,220, + 230,230,230,234, 0, 0, 9, 9, 0, 0, 7, 0,230,230,230, 0, 230, 0, 1, 1, 1, 0, 0, 0,230,234,214,220,202,230,230,230, 230,230,232,228,228,220,218,230,233,220,230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230,220,230, 1, 1, 0, 0,218,228, @@ -1683,63 +1482,81 @@ _hb_ucd_u8[17612] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 34, 9, 0, 0, 20, 20, 1, 20, 20, 0, 0, 0, 0, 0, 0, 0, 26, 21, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 47, 48, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 13, 13, 13, 13, 13, 14, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 15, 16, 17, 18, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 20, 20, 20, 20, 20, 20, - 20, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 20, 33, - 34, 35, 34, 34, 36, 37, 20, 20, 20, 20, 20, 20, 38, 20, 39, 40, - 41, 41, 41, 41, 41, 42, 43, 44, 20, 20, 20, 20, 20, 20, 20, 45, - 46, 20, 20, 47, 20, 20, 20, 48, 49, 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 20, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 11, 11, 11, 11, 12, 13, + 13, 13, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 13, 13, 13, + 24, 25, 26, 26, 26, 27, 13, 13, 13, 28, 29, 30, 13, 31, 32, 33, + 34, 35, 36, 37, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 38, 7, 7, 39, 7, 40, 7, 7, + 7, 41, 13, 42, 7, 7, 43, 7, 7, 7, 44, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 60, 13, 13, - 13, 61, 62, 13, 13, 13, 13, 63, 13, 13, 13, 13, 13, 13, 64, 65, - 20, 20, 66, 20, 13, 13, 13, 13, 67, 13, 13, 13, 68, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 45, 0, 0, 1, 2, 2, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 37, + 37, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 2, 2, 53, 54, 55, 56, 57, 58, 59, 59, 59, 59, 60, 59, + 59, 59, 59, 59, 59, 59, 61, 61, 59, 59, 59, 59, 62, 59, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 59, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 78, 69, 69, 69, 69, 79, 79, 79, 79, 79, 79, 79, 79, 79, 80, + 81, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 94, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 69, 69, 96, 97, 98, 99, 99, 99, + 100,101,102,103,104,105,106,107,108,109, 95,110,111,112,113,114, + 115,116,117,117,118,119,120,121,122,123,124,125,126,127,128,129, + 130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145, + 95,146,147,148,149, 95,150,151,152,153,154,155,156,157,158,159, + 160,161, 95,162,163,164,165,165,165,165,165,165,165,166,167,165, + 168, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95,169,170,170,170,170,170,170,170,170,171,170, + 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, + 170,170,170,170,170,170,170,170,170,170,170,170,170,172,173,173, + 173,173,174, 95, 95, 95, 95, 95,175, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95,176,176,176,176,177,178,179,180, 95, 95, + 181, 95,182,183,184,185,186,186,186,186,186,186,186,186,186,186, + 186,186,186,186,186,186,186,186,186,186,186,186,187,187,187,188, + 189,190, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95,191,192,193,194,195,195,196, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,197,198, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 59,199, + 59, 59, 59,200,201,202, 59,203,204,205,206,207,208, 95,209,210, + 211, 59, 59,212, 59,213,214,214,214,214,214,215, 95, 95, 95, 95, + 95, 95, 95, 95,216, 95,217,218,219, 95, 95,220, 95, 95, 95,221, + 95,222, 95,223, 95,224,225,226,227, 95, 95, 95, 95, 95,228,229, + 230, 95,231,232, 95, 95,233,234, 59,235,236, 95, 59, 59, 59, 59, + 59, 59, 59,237, 59,238,239,240, 59, 59,241,242, 59,243, 95, 95, + 95, 95, 95, 95, 95, 95, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69,244, 69, 69,245, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69,246, 69, 69, 69, 69, 69, 69, 69, 69, 69,247, 69, 69, + 69, 69,248, 95, 95, 95, 69, 69, 69, 69,249, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 69, 69, 69, 69, 69, 69,250, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69,251, 95, + 95, 95, 95, 95, 95, 95,252, 95,253,254, 0, 1, 2, 2, 0, 1, + 2, 2, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 19, @@ -1764,46 +1581,46 @@ _hb_ucd_u8[17612] = 64, 2, 2, 64, 64, 64, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 2, 2, 90, 90, 90, 90, 90, 90, 90, 2, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 2, 2, 95, 2, 37, 37, - 37, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, - 2, 2, 2, 2, 2, 3, 3, 3, 0, 3, 3, 3, 3, 3, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 7, 7, 7, 7, 7, - 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, - 5, 5, 5, 2, 2, 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 2, - 5, 2, 2, 2, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 5, 2, - 2, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 5, 2, 2, - 2, 2, 5, 5, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 2, 2, 11, 11, 11, 2, 11, 11, 11, 11, 11, - 11, 2, 2, 2, 2, 11, 11, 2, 2, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 11, 11, 11, 11, 11, 2, - 11, 11, 2, 11, 11, 2, 11, 11, 2, 2, 11, 2, 11, 11, 11, 2, - 2, 11, 11, 11, 2, 2, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, - 11, 11, 11, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 2, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 2, - 10, 10, 2, 10, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, 10, 10, - 2, 10, 10, 10, 2, 2, 10, 2, 2, 2, 2, 2, 2, 2, 10, 10, - 10, 10, 2, 2, 10, 10, 10, 10, 2, 2, 2, 2, 2, 2, 2, 10, - 10, 10, 10, 10, 10, 10, 2, 21, 21, 21, 2, 21, 21, 21, 21, 21, - 21, 21, 21, 2, 2, 21, 21, 2, 2, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 2, - 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 21, 21, 2, - 2, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 21, 21, 21, 2, 2, - 2, 2, 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, 2, - 22, 22, 2, 22, 22, 22, 22, 22, 22, 2, 2, 2, 22, 22, 22, 2, - 22, 22, 22, 22, 2, 2, 2, 22, 22, 2, 22, 2, 22, 22, 2, 2, - 2, 22, 22, 2, 2, 2, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 2, 2, 2, 2, 22, 22, 22, 2, 2, 2, 2, 2, 2, 22, 2, 2, - 2, 2, 2, 2, 22, 22, 22, 22, 22, 2, 2, 2, 2, 2, 23, 23, - 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 2, 23, 23, 23, 2, - 23, 23, 23, 23, 23, 23, 23, 23, 2, 2, 23, 23, 23, 23, 23, 2, - 23, 23, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 23, 2, 23, 23, - 23, 2, 2, 23, 2, 2, 23, 23, 23, 23, 2, 2, 23, 23, 2, 2, - 2, 2, 2, 2, 2, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 2, 16, 16, 16, 2, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 2, 16, 16, 16, 16, 16, 2, 2, 16, 16, 16, 16, 16, 2, - 16, 16, 16, 16, 2, 2, 2, 2, 2, 2, 2, 16, 16, 2, 16, 16, + 37, 2, 2, 2, 2, 2, 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, + 0, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, + 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 5, 5, + 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 2, + 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, + 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, + 2, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 2, 2, 2, + 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 5, 5, 2, 5, 5, 5, + 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 11, + 11, 11, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, + 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, + 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, + 2, 2, 11, 2, 11, 11, 11, 2, 2, 11, 11, 11, 2, 2, 2, 11, + 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, 2, 11, 2, 2, 2, + 2, 2, 2, 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 10, + 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, + 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, + 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, 10, + 2, 2, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 2, 2, 10, 2, + 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, + 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 2, 21, + 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, + 2, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, + 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 2, 21, 21, 21, 21, 21, + 2, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 2, 2, 2, 2, + 2, 2, 2, 21, 21, 21, 2, 2, 2, 2, 21, 21, 2, 21, 21, 21, + 21, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, + 22, 2, 2, 2, 22, 22, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, + 22, 2, 22, 2, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 2, 2, 2, 2, 22, 22, 22, 2, + 2, 2, 2, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, + 22, 2, 2, 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 2, 23, 23, 23, 2, 23, 23, 23, 23, 23, 23, 23, 23, + 2, 2, 23, 23, 23, 23, 23, 2, 23, 23, 23, 23, 2, 2, 2, 2, + 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 23, 23, 2, 2, 23, 23, + 23, 23, 2, 2, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 2, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 16, + 2, 2, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 2, 2, 2, 2, + 2, 2, 2, 16, 16, 2, 2, 2, 2, 2, 16, 16, 16, 2, 16, 16, 16, 16, 2, 2, 16, 16, 2, 16, 16, 16, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2, 20, 20, 20, 2, 20, 20, 20, 20, 20, 20, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, @@ -1850,137 +1667,135 @@ _hb_ucd_u8[17612] = 54, 54, 2, 2, 54, 54, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 2, 91, 91, 91, 91, 91, 2, 2, 91, 91, 91, 2, 2, 2, 2, 2, 2, 91, 91, 91, 91, 91, 91, 2, 2, 1, 1, - 1, 1, 1, 1, 1, 2, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 2, 62, 62, 76, 76, 76, 76, 76, 76, 76, 76, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 2, 2, 2, 2, 2, 2, - 2, 2, 93, 93, 93, 93, 70, 70, 70, 70, 70, 70, 70, 70, 2, 2, - 2, 70, 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 73, 73, - 73, 73, 73, 73, 73, 73, 6, 6, 6, 2, 2, 2, 2, 2, 8, 8, - 8, 2, 2, 8, 8, 8, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 19, 19, - 19, 19, 19, 19, 9, 9, 9, 9, 9, 6, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 9, 9, 9, 9, - 9, 19, 19, 19, 19, 19, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, 9, 9, 2, 2, 2, 9, - 2, 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, - 9, 9, 2, 2, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 2, 2, - 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 19, - 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, - 0, 0, 0, 0, 0, 2, 19, 19, 19, 19, 19, 2, 2, 2, 0, 2, - 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 9, 0, 0, 0, 19, 19, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 19, 0, 19, 0, 0, 0, 2, 2, 2, 2, 0, 0, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 27, 27, - 27, 27, 27, 27, 27, 27, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 0, 56, 56, 56, 56, 56, 56, 56, 56, 55, 55, - 55, 55, 2, 2, 2, 2, 2, 55, 55, 55, 55, 55, 55, 55, 61, 61, - 61, 61, 61, 61, 61, 61, 2, 2, 2, 2, 2, 2, 2, 61, 61, 2, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 2, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 2, 2, 0, 0, - 0, 0, 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 1, 1, 1, 1, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 2, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 2, 2, 1, 1, 0, 0, 15, 15, 15, 0, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 0, 0, 17, 17, 17, 2, 2, 2, 2, 2, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 0, 12, 12, - 12, 12, 12, 12, 12, 0, 17, 17, 17, 17, 17, 17, 17, 0, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 2, 2, 2, 39, 39, - 39, 39, 39, 39, 39, 2, 86, 86, 86, 86, 86, 86, 86, 86, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 2, 2, 2, 2, 79, 79, - 79, 79, 79, 79, 79, 79, 0, 0, 19, 19, 19, 19, 19, 19, 0, 0, - 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 2, 2, 19, 19, - 2, 19, 2, 19, 19, 19, 2, 2, 19, 19, 19, 19, 19, 19, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 2, 2, 2, 65, 65, - 65, 65, 65, 65, 65, 65, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, - 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 2, 2, 75, 75, 75, 75, - 2, 2, 2, 2, 2, 2, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 74, 12, 12, - 12, 12, 12, 2, 2, 2, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, - 84, 84, 84, 84, 2, 0, 84, 84, 2, 2, 2, 2, 84, 84, 33, 33, - 33, 33, 33, 33, 33, 2, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 2, 68, 68, 68, 68, 68, 68, 2, 2, 68, 68, - 2, 2, 68, 68, 68, 68, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, - 92, 2, 2, 2, 2, 2, 2, 2, 2, 92, 92, 92, 92, 92, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 2, 2, 30, - 30, 30, 30, 30, 30, 2, 19, 19, 19, 0, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 9, 19, 19, 19, 19, 0, 0, 2, 2, 2, 2, 87, 87, - 87, 87, 87, 87, 2, 2, 87, 87, 2, 2, 2, 2, 2, 2, 12, 12, - 12, 12, 2, 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 12, 13, 13, - 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, - 2, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 2, 14, 14, 14, 14, 14, 2, 14, 2, 14, 14, - 2, 14, 14, 2, 14, 14, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, - 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 2, 2, - 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 1, 1, - 1, 1, 1, 1, 6, 6, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, - 3, 3, 3, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 2, 2, - 12, 12, 12, 12, 12, 12, 2, 2, 12, 12, 12, 2, 2, 2, 2, 0, - 0, 0, 0, 0, 2, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, - 49, 2, 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 2, 2, 49, 49, - 49, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, - 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 2, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, 2, 2, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 2, 2, 2, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 2, 2, 2, 2, 2, 2, 2, 1, 0, - 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 2, 2, 2, 2, 2, 2, 2, 2, 2, 42, 42, 42, 41, 41, - 41, 41, 41, 41, 41, 41, 41, 41, 41, 2, 2, 2, 2, 2,118,118, - 118,118,118,118,118,118,118,118,118, 2, 2, 2, 2, 2, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 2, 53, 59, 59, - 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2, 2, 2, 2, 59, 59, - 59, 59, 59, 59, 2, 2, 40, 40, 40, 40, 40, 40, 40, 40, 51, 51, - 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 2, 2, 50, 50, 2, 2, 2, 2, 2, 2,135,135, - 135,135,135,135,135,135,135,135,135,135, 2, 2, 2, 2,106,106, - 106,106,106,106,106,106,104,104,104,104,104,104,104,104,104,104, - 104,104, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,104,161,161, - 161,161,161,161,161,161,161,161,161, 2,161,161,161,161,161,161, - 161, 2,161,161, 2,161,161,161, 2,161,161,161,161,161,161,161, - 2,161,161, 2, 2, 2,170,170,170,170,170,170,170,170,170,170, - 170,170, 2, 2, 2, 2,110,110,110,110,110,110,110,110,110,110, - 110,110,110,110,110, 2,110,110,110,110,110,110, 2, 2, 19, 19, - 19, 19, 19, 19, 2, 19, 19, 2, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 2, 2, 2, 2, 2, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, 2, 47, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2, 81,120,120, - 120,120,120,120,120,120,116,116,116,116,116,116,116,116,116,116, - 116,116,116,116,116, 2, 2, 2, 2, 2, 2, 2, 2,116,128,128, - 128,128,128,128,128,128,128,128,128, 2,128,128, 2, 2, 2, 2, - 2,128,128,128,128,128, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 2, 2, 2, 66, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 2, 2, 2, 2, 2, 72, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, - 97, 97, 97, 97, 97, 97, 2, 2, 2, 2, 97, 97, 97, 97, 2, 2, - 97, 97, 97, 97, 97, 97, 57, 57, 57, 57, 2, 57, 57, 2, 2, 2, - 2, 2, 57, 57, 57, 57, 57, 57, 57, 57, 2, 57, 57, 57, 2, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 2, 2, 57, 57, 57, 2, 2, 2, 2, 57, 57, 2, - 2, 2, 2, 2, 2, 2, 88, 88, 88, 88, 88, 88, 88, 88,117,117, - 117,117,117,117,117,117,112,112,112,112,112,112,112,112,112,112, - 112,112,112,112,112, 2, 2, 2, 2,112,112,112,112,112, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 2, 2, 2, 78, - 78, 78, 78, 78, 78, 78, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 83, 2, 2, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, - 82, 2, 2, 2, 2, 2,122,122,122,122,122,122,122,122,122,122, - 2, 2, 2, 2, 2, 2, 2,122,122,122,122, 2, 2, 2, 2,122, - 122,122,122,122,122,122, 89, 89, 89, 89, 89, 89, 89, 89, 89, 2, - 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,130,130,130,130, - 130, 2, 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,144,144, - 144,144,144,144,144,144,144,144, 2, 2, 2, 2, 2, 2,165,165, - 165,165,165,165,165,165,165,165,165,165,165,165, 2, 2, 2,165, - 165,165,165,165,165,165, 2, 2, 2, 2, 2, 2,165,165,156,156, + 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 2, 62, 62, 76, 76, + 76, 76, 76, 76, 76, 76, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 2, 2, 2, 2, 2, 2, 2, 2, 93, 93, 93, 93, 70, 70, + 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 70, 70, 70, 70, + 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 73, 73, 73, 73, 6, 6, + 6, 2, 2, 2, 2, 2, 8, 8, 8, 2, 2, 8, 8, 8, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, + 0, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, + 9, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, + 19, 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 19, 6, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, + 9, 9, 9, 9, 2, 2, 2, 9, 2, 9, 2, 9, 2, 9, 9, 9, + 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, + 9, 9, 2, 9, 9, 9, 2, 2, 9, 9, 9, 2, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, 0, 19, 2, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 2, 19, 19, + 19, 19, 19, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 1, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 19, 0, + 0, 0, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 27, 27, + 27, 27, 27, 27, 27, 27, 0, 0, 0, 0, 2, 2, 0, 0, 56, 56, + 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 2, 2, 2, 2, 2, 55, + 55, 55, 55, 55, 55, 55, 61, 61, 61, 61, 61, 61, 61, 61, 2, 2, + 2, 2, 2, 2, 2, 61, 61, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 13, 13, + 13, 13, 13, 13, 2, 2, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 13, 13, + 13, 13, 0, 0, 0, 0, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, + 1, 0, 0, 15, 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 17, 17, 17, 2, 2, + 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, + 2, 2, 2, 2, 2, 0, 12, 12, 12, 12, 12, 12, 12, 0, 17, 17, + 17, 17, 17, 17, 17, 0, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 2, 2, 2, 39, 39, 39, 39, 39, 39, 39, 2, 86, 86, + 86, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 2, 2, 2, 2, 79, 79, 79, 79, 79, 79, 79, 79, 0, 0, + 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 19, 19, 19, 19, 2, 19, + 19, 19, 19, 19, 19, 19, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 2, 2, 2, 65, 65, 65, 65, 65, 65, 65, 65, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 2, 2, 2, 2, + 2, 2, 2, 2, 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 0, 69, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 74, 12, 12, 12, 12, 12, 2, 2, 2, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 2, 0, 84, 84, + 2, 2, 2, 2, 84, 84, 33, 33, 33, 33, 33, 33, 33, 2, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 2, 68, 68, + 68, 68, 68, 68, 2, 2, 68, 68, 2, 2, 68, 68, 68, 68, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 2, 2, 2, 2, 2, 2, 2, + 2, 92, 92, 92, 92, 92, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 2, 2, 30, 30, 30, 30, 30, 30, 2, 19, 19, + 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 19, 19, 19, 19, + 0, 0, 2, 2, 2, 2, 87, 87, 87, 87, 87, 87, 2, 2, 87, 87, + 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, + 2, 12, 12, 12, 12, 12, 13, 13, 2, 2, 2, 2, 2, 2, 19, 19, + 19, 19, 19, 19, 19, 2, 2, 2, 2, 4, 4, 4, 4, 4, 2, 2, + 2, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 14, 14, + 14, 14, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, 14, 3, 3, + 3, 3, 3, 3, 0, 0, 1, 1, 1, 1, 1, 1, 6, 6, 0, 0, + 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 0, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, + 17, 17, 17, 17, 0, 0, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2, + 12, 12, 12, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 2, 49, 49, 49, 2, 49, 49, 2, 49, 49, 49, + 49, 49, 49, 49, 2, 2, 49, 49, 49, 2, 2, 2, 2, 2, 0, 0, + 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, + 0, 0, 0, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 1, 2, 2, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 67, 67, 67, 67, 2, + 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 2, 2, 2, 2, 2,118,118,118,118,118,118,118,118,118,118, + 118, 2, 2, 2, 2, 2, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 2, 2, 2, 2, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, + 40, 40, 40, 40, 40, 40, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 2, 2, 50, 50, + 2, 2, 2, 2, 2, 2,135,135,135,135,135,135,135,135,135,135, + 135,135, 2, 2, 2, 2,106,106,106,106,106,106,106,106,104,104, + 104,104,104,104,104,104,104,104,104,104, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,104,161,161,161,161,161,161,161,161,161,161, + 161, 2,161,161,161,161,161,161,161, 2,161,161, 2,161,161,161, + 2,161,161,161,161,161,161,161, 2,161,161, 2, 2, 2,170,170, + 170,170,170,170,170,170,170,170,170,170, 2, 2, 2, 2,110,110, + 110,110,110,110,110,110,110,110,110,110,110,110,110, 2,110,110, + 110,110,110,110, 2, 2, 19, 19, 19, 19, 19, 19, 2, 19, 19, 2, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 47, 47, + 47, 47, 47, 47, 2, 2, 47, 2, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 2, 47, 47, 2, + 2, 2, 47, 2, 2, 47, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 2, 81,120,120,120,120,120,120,120,120,116,116, + 116,116,116,116,116,116,116,116,116,116,116,116,116, 2, 2, 2, + 2, 2, 2, 2, 2,116,128,128,128,128,128,128,128,128,128,128, + 128, 2,128,128, 2, 2, 2, 2, 2,128,128,128,128,128, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 2, 2, 2, 66, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 2, 2, 2, 2, 2, 72,173,173, + 173,173,173,173,173,173,173,173, 2, 2, 2, 2, 2, 2, 98, 98, + 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 2, 2, + 2, 2, 97, 97, 97, 97, 2, 2, 97, 97, 97, 97, 97, 97, 57, 57, + 57, 57, 2, 57, 57, 2, 2, 2, 2, 2, 57, 57, 57, 57, 57, 57, + 57, 57, 2, 57, 57, 57, 2, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 2, 2, 57, 57, + 57, 2, 2, 2, 2, 57, 57, 2, 2, 2, 2, 2, 2, 2, 88, 88, + 88, 88, 88, 88, 88, 88,117,117,117,117,117,117,117,117,112,112, + 112,112,112,112,112,112,112,112,112,112,112,112,112, 2, 2, 2, + 2,112,112,112,112,112, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 2, 2, 2, 78, 78, 78, 78, 78, 78, 78, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 2, 2, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 2, 2, 2, 2, 2,122,122, + 122,122,122,122,122,122,122,122, 2, 2, 2, 2, 2, 2, 2,122, + 122,122,122, 2, 2, 2, 2,122,122,122,122,122,122,122, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 2, 2, 2, 2, 2, 2, 2,130,130, + 130,130,130,130,130,130,130,130,130, 2, 2, 2, 2, 2, 2, 2, + 130,130,130,130,130,130,144,144,144,144,144,144,144,144,144,144, + 2, 2, 2, 2, 2, 2,165,165,165,165,165,165,165,165,165,165, + 165,165,165,165, 2, 2, 2,165,165,165,165,165,165,165, 2, 2, + 2, 2, 2, 2,165,165, 3, 3, 3, 3, 3, 3, 3, 2,156,156, 156,156,156,156,156,156,156,156, 2,156,156,156, 2, 2,156,156, - 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, - 2, 2, 3, 3, 3, 3,147,147,147,147,147,147,147,147,148,148, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, + 2, 2, 2, 2, 2, 2,147,147,147,147,147,147,147,147,148,148, 148,148,148,148,148,148,148,148, 2, 2, 2, 2, 2, 2,158,158, 158,158,158,158,158,158,158,158, 2, 2, 2, 2, 2, 2,153,153, 153,153,153,153,153,153,153,153,153,153, 2, 2, 2, 2,149,149, @@ -2039,46 +1854,51 @@ _hb_ucd_u8[17612] = 143,143,143,143, 2,143,143, 2,143,143,143,143,143,143,143,143, 143,143,143,143,143,143,143,143,143,143,143,143,143, 2,143,143, 2,143,143,143,143,143,143, 2, 2, 2, 2, 2, 2, 2,143,143, - 2, 2, 2, 2, 2, 2,145,145,145,145,145,145,145,145,145, 2, - 2, 2, 2, 2, 2, 2,163,163,163,163,163,163,163,163,163, 2, - 163,163,163,163,163,163,163,163,163, 2, 2, 2,163,163,163,163, - 163, 2, 2, 2, 2, 2, 86, 2, 2, 2, 2, 2, 2, 2, 22, 22, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 22, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 2, 2, 2, 2, 2, 2, 63, 63, - 63, 63, 63, 63, 63, 2, 63, 63, 63, 63, 63, 2, 2, 2, 63, 63, - 63, 63, 2, 2, 2, 2,157,157,157,157,157,157,157,157,157,157, - 157, 2, 2, 2, 2, 2, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 2, 2, 80, 80, 80, 2, 2, 2, 2, 2,127,127, - 127,127,127,127,127,127,127,127,127,127,127,127,127, 2,166,166, - 166,166,166,166,166,166,166,166, 2, 2, 2, 2, 2, 2, 79, 2, - 2, 2, 2, 2, 2, 2,115,115,115,115,115,115,115,115,115,115, - 115,115,115,115,115, 2,115,115, 2, 2, 2, 2,115,115,159,159, - 159,159,159,159,159,159,159,159,159,159,159,159,159, 2,159,159, - 2, 2, 2, 2, 2, 2,103,103,103,103,103,103,103,103,103,103, - 103,103,103,103, 2, 2,119,119,119,119,119,119,119,119,119,119, - 119,119,119,119, 2, 2,119,119, 2,119,119,119,119,119, 2, 2, - 2, 2, 2,119,119,119,167,167,167,167,167,167,167,167,167,167, - 2, 2, 2, 2, 2, 2,146,146,146,146,146,146,146,146,146,146, - 146, 2, 2, 2, 2, 2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 2, 2, 2, 2, 2, 2,175,175,175,175,175,175,175,175,175,175, + 175,175, 2, 2, 2, 2,175,175, 2, 2, 2, 2, 2, 2,145,145, + 145,145,145,145,145,145,145, 2, 2, 2, 2, 2, 2, 2,163,163, + 163,163,163,163,163,163,163, 2,163,163,163,163,163,163,163,163, + 163, 2, 2, 2,163,163,163,163,163, 2, 2, 2, 2, 2, 86, 2, + 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 22, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 2, 63, 63, + 63, 63, 63, 2, 2, 2, 63, 63, 63, 63, 2, 2, 2, 2,157,157, + 157,157,157,157,157,157,157,157,157, 2, 2, 2, 2, 2, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 2, 2, 80, 80, + 80, 2, 2, 2, 2, 2,127,127,127,127,127,127,127,127,127,127, + 127,127,127,127,127, 2,166,166,166,166,166,166,166,166,166,166, + 2, 2, 2, 2, 2, 2, 79, 2, 2, 2, 2, 2, 2, 2,115,115, + 115,115,115,115,115,115,115,115,115,115,115,115,115, 2,115,115, + 2, 2, 2, 2,115,115,159,159,159,159,159,159,159,159,159,159, + 159,159,159,159,159, 2,159,159, 2, 2, 2, 2, 2, 2,103,103, + 103,103,103,103,103,103,103,103,103,103,103,103, 2, 2,119,119, + 119,119,119,119,119,119,119,119,119,119,119,119, 2, 2,119,119, + 2,119,119,119,119,119, 2, 2, 2, 2, 2,119,119,119,167,167, + 167,167,167,167,167,167,167,167, 2, 2, 2, 2, 2, 2,146,146, + 146,146,146,146,146,146,146,146,146, 2, 2, 2, 2, 2,172,172, + 172,172,172,172,172,172,172, 2, 2,172,172,172,172,172,172,172, + 172,172, 2, 2, 2, 2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, 2, 99, 2, 2, 2, 2, 2, 2, 2, 99,136,139, - 13, 13,155, 2, 2, 2,136,136,136,136,136,136,136,136,155,155, - 155,155,155,155,155,155,155,155,155,155,155,155, 2, 2, 2, 2, - 2, 2, 2, 2, 2,155,136, 2, 2, 2, 2, 2, 2, 2, 17, 17, + 13, 13,155, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 2,136,136, + 136,136,136,136,136,136,155,155,155,155,155,155,155,155,155,155, + 155,155,155,155, 2, 2, 2, 2, 2, 2, 2, 2, 2,155,136,136, + 136,136,136,136,136, 2,136,136,136, 2, 2, 2, 2, 2, 17, 17, 17, 17, 2, 17, 17, 17, 17, 17, 17, 17, 2, 17, 17, 2, 17, 15, 15, 15, 15, 15, 15, 15, 17, 17, 17, 2, 2, 2, 2, 2, 2, 2, 15, 2, 2, 2, 2, 2, 15, 15, 15, 2, 2, 17, 2, 2, 2, 2, 2, 2, 17, 17, 17, 17,139,139,139,139,139,139,139,139,139,139, 139,139, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105, 105, 2, 2, 2, 2, 2,105,105,105,105,105, 2, 2, 2,105, 2, - 2, 2, 2, 2, 2, 2,105,105, 2, 2,105,105,105,105, 1, 1, - 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 2, 2, 2, 2, 2, 2,105,105, 2, 2,105,105,105,105, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 2, 0, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, - 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0,131,131, + 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0,131,131, 131,131,131,131,131,131,131,131,131,131, 2, 2, 2, 2, 2, 2, 2,131,131,131,131,131, 2,131,131,131,131,131,131,131, 2, 2, 2, 2, 2, 19, 19, 19, 56, 56, 56, 56, 56, 56, 56, 2, 56, 2, @@ -2090,7 +1910,9 @@ _hb_ucd_u8[17612] = 160,160,160,160,160, 2,152,152,152,152,152,152,152,152,152,152, 2, 2, 2, 2, 2,152,164,164,164,164,164,164,164,164,164,164, 2, 2, 2, 2, 2, 2,168,168,168,168,168,168,168,168,168,168, - 168, 2, 2, 2, 2,168, 30, 30, 30, 30, 2, 30, 30, 2,113,113, + 168, 2, 2, 2, 2,168,174,174,174,174,174,174,174,174,174,174, + 174,174,174,174,174, 2,174,174,174,174,174,174, 2, 2, 2, 2, + 2, 2, 2, 2,174,174, 30, 30, 30, 30, 2, 30, 30, 2,113,113, 113,113,113,113,113,113,113,113,113,113,113, 2, 2,113,113,113, 113,113,113,113,113, 2,132,132,132,132,132,132,132,132,132,132, 132,132, 2, 2, 2, 2,132,132, 2, 2, 2, 2,132,132, 3, 3, @@ -2101,8 +1923,8 @@ _hb_ucd_u8[17612] = 3, 3, 3, 2, 3, 2, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 15, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, - 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 13, 2, 2, 2, 2, 2, 2, 2, 13, 13, 13, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, 9, 11, 12, 13, @@ -2186,8 +2008,7 @@ _hb_ucd_u8[17612] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, }; -static const uint16_t -_hb_ucd_u16[10400] = +static const uint16_t _hb_ucd_u16[10832]= { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, @@ -2210,27 +2031,29 @@ _hb_ucd_u16[10400] = 48, 48, 48, 48, 168, 169, 48, 48, 168, 48, 48, 170, 171, 172, 48, 48, 48, 171, 48, 48, 48, 173, 174, 175, 48, 176, 9, 9, 9, 9, 9, 177, 178, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 179, 48, 180, 181, 48, 48, 48, 48, 182, 183, 48, 184, 48, 185, 48, 186, 187, 188, 48, 48, 48, 189, 190, 191, 192, 193, 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, 48, 200, 201, 202, 203, 48, 204, 205, 48, 48, 206, 48, 207, 208, 209, 209, - 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 216, 140, 140, 140, - 217, 48, 48, 218, 219, 160, 220, 221, 222, 48, 223, 64, 48, 48, 224, 225, - 48, 48, 226, 227, 228, 64, 48, 229, 230, 9, 9, 231, 232, 233, 234, 235, - 11, 11, 236, 27, 27, 27, 237, 238, 11, 239, 27, 27, 32, 32, 32, 32, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 240, 13, 13, 13, 13, 13, 13, - 241, 242, 241, 241, 242, 243, 241, 244, 245, 245, 245, 246, 247, 248, 249, 250, - 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 261, 262, 263, 264, 265, - 266, 267, 268, 269, 270, 271, 272, 272, 273, 274, 275, 209, 276, 277, 209, 278, - 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, - 280, 209, 281, 209, 209, 209, 209, 282, 209, 283, 279, 284, 209, 285, 286, 209, - 209, 209, 176, 140, 287, 140, 271, 271, 271, 288, 209, 209, 209, 209, 289, 271, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 290, 291, 209, 209, 292, - 209, 209, 209, 209, 209, 209, 293, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 294, 295, 271, 296, 209, 209, 297, 279, 298, 279, + 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 32, 216, 217, 140, + 218, 48, 48, 219, 220, 160, 221, 222, 223, 48, 224, 64, 48, 48, 225, 226, + 48, 48, 227, 228, 229, 64, 48, 230, 231, 9, 9, 232, 233, 234, 235, 236, + 11, 11, 237, 27, 27, 27, 238, 239, 11, 240, 27, 27, 32, 32, 32, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 241, 13, 13, 13, 13, 13, 13, + 242, 243, 242, 242, 243, 244, 242, 245, 246, 246, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 273, 274, 275, 276, 209, 277, 278, 209, 279, + 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 281, 209, 282, 209, 209, 209, 209, 283, 209, 284, 280, 285, 209, 286, 287, 209, + 209, 209, 176, 140, 288, 140, 272, 272, 272, 289, 209, 209, 209, 209, 290, 272, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 291, 292, 209, 209, 293, + 209, 209, 209, 209, 209, 209, 294, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 295, 296, 272, 297, 209, 209, 298, 280, 299, 280, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 279, 279, 279, 279, 279, 279, 279, 279, 299, 300, 279, 279, 279, 301, 279, 302, - 209, 209, 209, 279, 303, 209, 209, 304, 209, 305, 209, 209, 209, 209, 209, 209, + 280, 280, 280, 280, 280, 280, 280, 280, 300, 301, 280, 280, 280, 302, 280, 303, + 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 209, 209, 209, 280, 304, 209, 209, 305, 209, 209, 209, 209, 209, 209, 209, 209, 9, 9, 9, 11, 11, 11, 306, 307, 13, 13, 13, 13, 13, 13, 308, 309, 11, 11, 310, 48, 48, 48, 311, 312, 48, 313, 314, 314, 314, 314, 32, 32, 315, 316, 317, 318, 319, 320, 140, 140, 209, 321, 209, 209, 209, 209, 209, 322, @@ -2238,226 +2061,201 @@ _hb_ucd_u16[10400] = 324, 325, 326, 327, 136, 48, 48, 48, 48, 328, 178, 48, 48, 48, 48, 329, 330, 48, 48, 136, 48, 48, 48, 48, 200, 331, 48, 48, 209, 209, 332, 48, 209, 333, 334, 209, 335, 336, 209, 209, 334, 209, 209, 336, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 209, 209, 209, 209, 48, 337, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 338, 48, 48, 229, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 338, 48, 48, 230, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 339, 48, 340, 140, 13, 13, 341, 342, 13, 343, 48, 48, 48, 48, 344, 345, 31, 346, 347, 348, 13, 13, 13, 349, 350, 351, 352, 353, 354, 355, 140, 356, 357, 48, 358, 359, 48, 48, 48, 360, 361, 48, 48, 362, 363, 192, 32, 364, 64, 48, 365, 48, 366, 367, 48, 151, 76, 48, 48, 368, 369, 370, 371, 372, 48, 48, 373, 374, 375, 376, 48, 377, 48, 48, 48, 378, 379, 380, 381, 382, 383, 384, 314, 11, 11, 385, 386, 11, 11, 11, 11, 11, 48, 48, 387, 192, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 388, 48, 389, 48, 48, 206, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 207, 140, 140, 392, 393, 394, 395, 396, 48, 48, 48, 48, 48, 48, 397, 398, 399, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 400, 209, 48, 48, 48, 48, 401, 48, 48, 402, 140, 140, 403, 32, 404, 32, 405, 406, 407, 408, 409, 48, 48, 48, 48, 48, 48, 48, 410, 411, 2, 3, 4, 5, 412, 413, 414, 48, 415, 48, 200, 416, 417, 418, 419, 420, 48, 172, 421, 204, 204, 140, 140, 48, 48, 48, 48, 48, 48, 48, 71, - 422, 271, 271, 423, 272, 272, 272, 424, 425, 426, 427, 140, 140, 209, 209, 428, + 422, 272, 272, 423, 273, 273, 273, 424, 425, 426, 427, 140, 140, 209, 209, 428, 140, 140, 140, 140, 140, 140, 140, 140, 48, 151, 48, 48, 48, 100, 429, 430, 48, 48, 431, 48, 432, 48, 48, 433, 48, 434, 48, 48, 435, 436, 140, 140, 9, 9, 437, 11, 11, 48, 48, 48, 48, 204, 192, 9, 9, 438, 11, 439, 48, 48, 440, 48, 48, 48, 441, 442, 442, 443, 444, 445, 48, 48, 48, 388, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 313, 48, 199, 440, 140, 446, 27, 27, 447, 140, 140, 140, 140, 448, 48, 48, 449, 48, 450, 48, 451, 48, 200, 452, 140, 140, 140, 48, 453, - 48, 454, 48, 455, 140, 140, 140, 140, 48, 48, 48, 456, 271, 457, 271, 271, + 48, 454, 48, 455, 48, 207, 140, 140, 48, 48, 48, 456, 272, 457, 272, 272, 458, 459, 48, 460, 461, 462, 48, 463, 48, 464, 140, 140, 465, 48, 466, 467, 48, 48, 48, 468, 48, 469, 48, 470, 48, 471, 472, 140, 140, 140, 140, 140, 48, 48, 48, 48, 196, 140, 140, 140, 9, 9, 9, 473, 11, 11, 11, 474, 48, 48, 475, 192, 476, 9, 477, 11, 478, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 271, 479, 48, 48, 480, 481, 482, 140, 140, 483, - 48, 464, 484, 48, 62, 485, 140, 48, 486, 140, 140, 48, 487, 140, 48, 313, - 488, 48, 48, 489, 490, 457, 491, 492, 222, 48, 48, 493, 494, 48, 196, 192, - 495, 48, 496, 497, 498, 48, 48, 499, 222, 48, 48, 500, 501, 502, 503, 504, - 48, 97, 505, 506, 507, 140, 140, 140, 508, 509, 510, 48, 48, 511, 512, 192, - 513, 83, 84, 514, 515, 516, 517, 518, 519, 48, 48, 520, 521, 522, 523, 140, - 48, 48, 48, 524, 525, 526, 481, 140, 48, 48, 48, 527, 528, 192, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 529, 530, 531, 532, 140, 140, - 48, 48, 48, 533, 534, 192, 535, 140, 48, 48, 536, 537, 192, 538, 539, 140, - 48, 540, 541, 542, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 505, 543, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 544, - 545, 546, 48, 547, 548, 192, 140, 140, 140, 140, 549, 48, 48, 550, 551, 140, - 552, 48, 48, 553, 554, 555, 48, 48, 556, 557, 558, 48, 48, 48, 48, 196, - 559, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 560, 192, - 84, 48, 529, 561, 562, 148, 175, 563, 48, 564, 565, 566, 140, 140, 140, 140, - 567, 48, 48, 568, 569, 192, 570, 48, 571, 572, 192, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 573, - 574, 115, 48, 575, 576, 577, 140, 140, 140, 140, 140, 100, 271, 578, 579, 580, + 140, 140, 140, 140, 140, 140, 272, 479, 48, 48, 480, 481, 482, 483, 140, 484, + 48, 464, 485, 48, 62, 486, 140, 48, 487, 140, 140, 48, 488, 140, 48, 313, + 489, 48, 48, 490, 491, 457, 492, 493, 223, 48, 48, 494, 495, 48, 196, 192, + 496, 48, 497, 498, 499, 48, 48, 500, 223, 48, 48, 501, 502, 503, 504, 505, + 48, 97, 506, 507, 508, 140, 140, 140, 509, 510, 511, 48, 48, 512, 513, 192, + 514, 83, 84, 515, 516, 517, 518, 519, 520, 48, 48, 521, 522, 523, 524, 140, + 48, 48, 48, 525, 526, 527, 481, 140, 48, 48, 48, 528, 529, 192, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 530, 531, 532, 533, 140, 140, + 48, 48, 48, 534, 535, 192, 536, 140, 48, 48, 537, 538, 192, 539, 540, 140, + 48, 541, 542, 543, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 506, 544, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 545, + 546, 547, 48, 548, 549, 192, 140, 140, 140, 140, 550, 48, 48, 551, 552, 140, + 553, 48, 48, 554, 555, 556, 48, 48, 557, 558, 559, 48, 48, 48, 48, 196, + 560, 140, 140, 140, 140, 140, 561, 140, 140, 140, 140, 140, 48, 48, 562, 192, + 84, 48, 530, 563, 564, 148, 175, 565, 48, 566, 567, 568, 140, 140, 140, 140, + 569, 48, 48, 570, 571, 192, 572, 48, 573, 574, 192, 48, 48, 575, 192, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 576, + 577, 115, 48, 578, 579, 580, 140, 140, 140, 140, 140, 100, 272, 581, 582, 583, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 207, 140, 140, 140, 140, 140, 140, - 272, 272, 272, 272, 272, 272, 581, 582, 48, 48, 48, 48, 48, 48, 48, 48, + 273, 273, 273, 273, 273, 273, 584, 585, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 388, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 583, - 48, 48, 48, 584, 585, 586, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 586, + 48, 48, 48, 587, 588, 589, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 71, 48, 48, 48, 48, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 587, 588, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 589, - 48, 48, 48, 590, 591, 592, 593, 594, 48, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 595, 48, 596, 192, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 9, 9, 11, 11, 271, 597, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 48, 598, 599, 600, 600, 601, 602, 140, 140, 140, 140, 603, 604, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 440, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 199, 140, 605, - 196, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 606, - 48, 48, 607, 608, 140, 609, 610, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 590, 591, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 592, + 48, 48, 48, 593, 594, 595, 596, 597, 48, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 598, 48, 599, 192, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 9, 9, 11, 11, 272, 600, 9, 601, 11, 602, 140, 140, + 48, 48, 48, 48, 603, 604, 605, 605, 606, 607, 140, 140, 140, 140, 608, 609, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 199, 140, 610, + 48, 200, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 48, 611, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 612, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 611, 613, 140, 614, 615, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 206, - 48, 48, 48, 48, 48, 48, 71, 151, 196, 611, 612, 140, 140, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 192, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, 140, - 32, 32, 613, 32, 614, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 48, 48, 71, 151, 196, 616, 617, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 618, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 619, 209, 427, 209, 620, + 32, 32, 216, 32, 621, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 323, - 209, 209, 615, 209, 209, 209, 616, 617, 618, 209, 619, 209, 209, 209, 287, 140, - 209, 209, 209, 209, 620, 140, 140, 140, 140, 140, 140, 140, 271, 621, 271, 621, - 209, 209, 209, 209, 209, 338, 271, 461, 140, 140, 140, 140, 140, 140, 140, 140, - 9, 622, 11, 623, 624, 625, 241, 9, 626, 627, 628, 629, 630, 9, 622, 11, - 631, 632, 11, 633, 634, 635, 636, 9, 637, 11, 9, 622, 11, 623, 624, 11, - 241, 9, 626, 636, 9, 637, 11, 9, 622, 11, 638, 9, 639, 640, 641, 642, - 11, 643, 9, 644, 645, 646, 647, 11, 648, 9, 649, 11, 650, 538, 538, 538, - 32, 32, 32, 651, 32, 32, 652, 653, 654, 655, 45, 140, 140, 140, 140, 140, - 656, 657, 658, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 659, 660, 661, 27, 27, 27, 662, 140, 663, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 151, 664, 665, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 666, 140, 48, 48, 667, 668, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 669, 192, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 587, 670, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 671, 200, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 672, 614, 140, 140, - 9, 9, 626, 11, 673, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 503, 271, 271, 674, 675, 140, 140, 140, 140, - 503, 271, 676, 677, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 678, 48, 679, 680, 681, 682, 683, 684, 685, 206, 686, 206, 140, 140, 140, 687, - 209, 209, 688, 209, 209, 209, 209, 209, 209, 322, 333, 689, 689, 689, 209, 323, - 690, 209, 209, 209, 209, 209, 209, 209, 209, 209, 691, 140, 140, 140, 692, 209, - 693, 209, 209, 688, 694, 695, 323, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 696, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 697, 426, 426, - 209, 209, 209, 209, 209, 209, 209, 698, 209, 209, 209, 209, 209, 176, 688, 427, - 688, 209, 209, 209, 699, 176, 209, 209, 699, 209, 691, 688, 695, 140, 140, 140, - 209, 209, 209, 209, 209, 322, 691, 426, 700, 209, 209, 209, 701, 702, 176, 694, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 703, 209, 209, 209, 209, 209, 192, + 209, 209, 622, 209, 209, 209, 623, 624, 625, 209, 626, 209, 209, 209, 288, 140, + 209, 209, 209, 209, 627, 140, 140, 140, 140, 140, 140, 140, 272, 628, 272, 628, + 209, 209, 209, 209, 209, 338, 272, 461, 140, 140, 140, 140, 140, 140, 140, 140, + 9, 629, 11, 630, 631, 632, 242, 9, 633, 634, 635, 636, 637, 9, 629, 11, + 638, 639, 11, 640, 641, 642, 643, 9, 644, 11, 9, 629, 11, 630, 631, 11, + 242, 9, 633, 643, 9, 644, 11, 9, 629, 11, 645, 9, 646, 647, 648, 649, + 11, 650, 9, 651, 652, 653, 654, 11, 655, 9, 656, 11, 657, 539, 539, 539, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 32, 32, 32, 658, 32, 32, 659, 660, 661, 662, 45, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 663, 664, 665, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 666, 667, 668, 27, 27, 27, 669, 140, 670, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 151, 671, 672, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 673, 140, 48, 48, 674, 675, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 676, 192, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 590, 677, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 200, 678, 679, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 680, 200, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 681, 621, 140, 140, + 9, 9, 633, 11, 682, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 504, 272, 272, 683, 684, 140, 140, 140, 140, + 504, 272, 685, 686, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 687, 48, 688, 689, 690, 691, 692, 693, 694, 206, 695, 206, 140, 140, 140, 696, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 209, 209, 697, 209, 209, 209, 209, 209, 209, 322, 333, 698, 698, 698, 209, 323, + 699, 209, 209, 209, 209, 209, 209, 209, 209, 209, 700, 140, 140, 140, 701, 209, + 702, 209, 209, 697, 703, 704, 323, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 705, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 706, 426, 426, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 176, 697, 427, + 697, 209, 209, 209, 707, 176, 209, 209, 707, 209, 700, 697, 704, 708, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 707, 700, 426, 709, 209, 209, 209, 710, 711, 712, 703, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 713, 209, 209, 209, 209, 209, 714, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 140, 140, - 48, 48, 48, 207, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 481, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 100, 48, 48, 48, 48, 48, 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 48, 71, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 140, 140, 140, 140, 140, - 704, 140, 584, 584, 584, 584, 584, 584, 140, 140, 140, 140, 140, 140, 140, 140, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 140, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 705, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 706, - 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10, - 11, 11, 12, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - 57, 57, 58, 59, 60, 60, 60, 60, 61, 62, 63, 64, 65, 66, 67, 68, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 70, 71, 72, 73, 74, 75, - 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, - 92, 93, 94, 95, 96, 97, 98, 7, 4, 4, 4, 4, 99, 100, 101, 102, - 103, 104, 105, 106, 107, 108, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 110, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 112, 112, 112, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 114, 0, - 115, 116, 117, 118, 119, 120, 121, 122, 0, 123, 124, 125, 126, 126, 126, 127, - 128, 129, 130, 131, 132, 60, 133, 134, 135, 136, 0, 137, 138, 139, 0, 0, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 0, 126, 126, 126, 126, 126, 126, 126, 126, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 141, 142, 143, 143, 143, 143, 144, 11, 145, 146, 147, 4, 148, 149, - 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 166, 167, - 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, - 168, 168, 168, 168, 126, 126, 126, 126, 126, 169, 126, 170, 171, 172, 19, 173, - 19, 19, 19, 19, 174, 19, 175, 176, 177, 178, 19, 179, 180, 181, 182, 183, - 184, 185, 186, 187, 188, 189, 190, 191, 168, 168, 192, 193, 194, 195, 196, 197, - 198, 199, 200, 201, 202, 203, 204, 205, 206, 206, 206, 206, 207, 208, 209, 168, - 210, 211, 212, 213, 214, 168, 215, 216, 217, 218, 219, 220, 221, 222, 223, 168, - 224, 225, 226, 227, 228, 229, 230, 168, 168, 231, 232, 233, 234, 235, 236, 237, - 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, - 254, 255, 256, 257, 168, 168, 258, 259, 260, 261, 262, 263, 264, 265, 168, 168, - 266, 168, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 168, 168, 278, - 279, 280, 281, 168, 282, 283, 284, 168, 168, 168, 168, 285, 286, 287, 288, 289, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 291, 168, - 290, 292, 290, 290, 290, 293, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, - 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 294, 295, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, - 296, 297, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 298, - 299, 299, 299, 299, 299, 299, 299, 299, 299, 300, 168, 168, 168, 168, 168, 168, - 168, 168, 168, 168, 301, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, - 302, 302, 302, 302, 302, 302, 302, 302, 303, 304, 305, 306, 307, 308, 309, 168, - 168, 168, 168, 168, 168, 310, 168, 168, 168, 311, 312, 168, 313, 314, 315, 316, - 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, - 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 318, - 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 319, 319, 319, 319, - 319, 319, 319, 320, 321, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, - 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 322, - 323, 324, 324, 324, 325, 326, 327, 327, 327, 327, 327, 328, 168, 168, 168, 168, - 329, 330, 331, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, - 0, 0, 0, 332, 0, 0, 0, 0, 0, 0, 333, 168, 334, 335, 0, 336, - 0, 0, 0, 337, 338, 339, 340, 341, 189, 342, 168, 343, 0, 344, 168, 168, - 0, 345, 346, 347, 348, 349, 0, 0, 0, 0, 350, 0, 0, 0, 0, 351, - 352, 352, 352, 352, 352, 352, 352, 352, 352, 352, 353, 168, 168, 168, 168, 168, - 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 354, 168, 168, 168, - 355, 356, 357, 168, 358, 359, 168, 168, 168, 168, 360, 361, 168, 168, 168, 168, - 168, 168, 168, 362, 168, 168, 168, 363, 168, 168, 168, 168, 168, 168, 168, 364, - 365, 365, 365, 366, 367, 368, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, - 168, 369, 370, 168, 371, 168, 168, 168, 372, 373, 374, 375, 168, 168, 168, 168, - 376, 0, 377, 378, 0, 0, 379, 380, 381, 382, 168, 168, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 383, 0, 384, 0, 385, - 386, 387, 388, 389, 0, 0, 0, 0, 0, 390, 391, 392, 0, 0, 393, 332, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 394, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 395, 126, 126, 126, - 396, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 397, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 398, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 399, 168, 168, 168, 168, 168, 168, - 126, 126, 126, 126, 126, 126, 126, 126, 399, 168, 168, 168, 168, 168, 168, 168, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 400, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 401, 168, - 402, 0, 168, 168, 7, 7, 7, 403, 0, 1, 2, 3, 4, 4, 4, 4, + 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 71, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 207, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 715, 140, 587, 587, 587, 587, 587, 587, 140, 140, 140, 140, 140, 140, 140, 140, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 140, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 716, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 717, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 1, 2, 2, 3, 0, 0, 0, 0, 0, 4, 0, 4, 2, 2, 5, 2, 2, 2, 5, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, - 0, 0, 0, 0, 7, 8, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 10, 11, 12, 13, 14, 14, 15, 14, 14, 14, - 14, 14, 14, 14, 16, 17, 14, 14, 18, 18, 18, 18, 18, 18, 18, 18, - 19, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 20, 21, - 21, 21, 22, 20, 21, 21, 21, 21, 21, 23, 24, 25, 25, 25, 25, 25, - 25, 26, 25, 25, 25, 27, 28, 26, 29, 30, 31, 32, 31, 31, 31, 31, - 33, 34, 35, 31, 31, 31, 36, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 29, 31, 31, 31, 31, 37, 38, 37, 37, 37, 37, 37, 37, - 37, 39, 31, 31, 31, 31, 31, 31, 40, 40, 40, 40, 40, 40, 41, 26, - 42, 42, 42, 42, 42, 42, 42, 43, 44, 44, 44, 44, 44, 45, 44, 46, - 47, 47, 47, 48, 37, 49, 31, 31, 31, 50, 51, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 52, 31, 31, 31, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 54, 53, 55, 53, 53, 53, 56, 57, 58, 59, 59, 60, 61, 62, - 57, 63, 64, 65, 66, 59, 59, 67, 68, 69, 70, 71, 71, 72, 73, 74, - 69, 75, 76, 77, 78, 71, 79, 26, 80, 81, 82, 83, 83, 84, 85, 86, - 81, 87, 88, 26, 89, 83, 90, 91, 92, 93, 94, 95, 95, 96, 97, 98, - 93, 99, 100, 101, 102, 95, 95, 26, 103, 104, 105, 106, 107, 104, 108, 109, - 104, 105, 110, 26, 111, 108, 108, 112, 113, 114, 115, 113, 113, 115, 113, 116, - 114, 117, 118, 119, 120, 113, 121, 113, 122, 123, 124, 122, 122, 124, 125, 126, - 123, 127, 128, 128, 129, 122, 130, 26, 131, 132, 133, 131, 131, 131, 131, 131, - 132, 133, 134, 131, 135, 131, 131, 131, 136, 137, 138, 139, 137, 137, 140, 141, - 138, 142, 143, 137, 144, 137, 145, 26, 146, 147, 147, 147, 147, 147, 147, 148, - 147, 147, 147, 149, 26, 26, 26, 26, 150, 151, 152, 152, 153, 152, 152, 154, - 155, 156, 152, 157, 26, 26, 26, 26, 158, 158, 158, 158, 158, 158, 158, 158, - 158, 159, 158, 158, 158, 160, 159, 158, 158, 158, 158, 159, 158, 158, 158, 161, - 158, 161, 162, 163, 26, 26, 26, 26, 164, 164, 164, 164, 164, 164, 164, 164, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 6, 0, 0, 0, 0, 7, 8, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 11, + 12, 13, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, 16, 17, 14, 14, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 20, 21, 21, 21, 22, 20, 21, 21, 21, 21, + 21, 23, 24, 25, 25, 25, 25, 25, 25, 26, 25, 25, 25, 27, 28, 26, + 29, 30, 31, 32, 31, 31, 31, 31, 33, 34, 35, 31, 31, 31, 36, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 29, 31, 31, 31, 31, + 37, 38, 37, 37, 37, 37, 37, 37, 37, 39, 31, 31, 31, 31, 31, 31, + 40, 40, 40, 40, 40, 40, 41, 26, 42, 42, 42, 42, 42, 42, 42, 43, + 44, 44, 44, 44, 44, 45, 44, 46, 47, 47, 47, 48, 37, 49, 31, 31, + 31, 31, 50, 31, 31, 31, 31, 31, 31, 31, 31, 31, 51, 31, 31, 31, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 52, 54, 52, 52, 52, + 55, 56, 57, 58, 58, 59, 60, 61, 56, 62, 63, 64, 65, 58, 58, 66, + 67, 68, 69, 70, 70, 71, 72, 73, 68, 74, 75, 76, 77, 70, 78, 26, + 79, 80, 81, 82, 82, 83, 84, 85, 80, 86, 87, 26, 88, 82, 89, 90, + 91, 92, 93, 94, 94, 95, 96, 97, 92, 98, 99, 100, 101, 94, 94, 26, + 102, 103, 104, 105, 106, 103, 107, 108, 103, 104, 109, 26, 110, 107, 107, 111, + 112, 113, 114, 112, 112, 114, 112, 115, 113, 116, 117, 118, 119, 112, 120, 112, + 121, 122, 123, 121, 121, 123, 124, 125, 122, 126, 127, 128, 129, 121, 130, 26, + 131, 132, 133, 131, 131, 131, 131, 131, 132, 133, 134, 131, 135, 131, 131, 131, + 136, 137, 138, 139, 137, 137, 140, 141, 138, 142, 143, 137, 144, 137, 145, 26, + 146, 147, 147, 147, 147, 147, 147, 148, 147, 147, 147, 149, 26, 26, 26, 26, + 150, 151, 152, 152, 153, 152, 152, 154, 155, 156, 152, 157, 26, 26, 26, 26, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 159, 158, 158, 158, 160, 159, 158, + 158, 158, 158, 159, 158, 158, 158, 161, 158, 161, 162, 163, 26, 26, 26, 26, + 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 166, 167, 165, 165, 165, 165, 165, 168, - 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 170, 170, - 170, 171, 172, 171, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 171, 172, - 171, 170, 172, 170, 170, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 170, - 170, 170, 170, 173, 170, 170, 170, 174, 170, 170, 170, 175, 176, 176, 176, 176, - 176, 176, 176, 176, 176, 176, 177, 177, 178, 178, 178, 178, 178, 178, 178, 178, + 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, + 170, 170, 170, 170, 170, 170, 170, 170, 170, 171, 172, 171, 170, 170, 170, 170, + 170, 171, 170, 170, 170, 170, 171, 172, 171, 170, 172, 170, 170, 170, 170, 170, + 170, 170, 171, 170, 170, 170, 170, 170, 170, 170, 170, 173, 170, 170, 170, 174, + 170, 170, 170, 175, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 177, 177, + 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 179, 179, 179, 180, 181, 181, 181, 181, 181, 181, 181, 181, 181, 182, 181, 183, 184, 184, 185, 186, 187, 187, 188, 26, 189, 189, 190, 26, 191, 192, 193, 26, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 195, 194, 196, 194, 196, @@ -2466,165 +2264,215 @@ _hb_ucd_u16[10400] = 203, 203, 203, 204, 203, 205, 203, 205, 206, 203, 207, 207, 207, 208, 209, 26, 210, 210, 210, 210, 210, 211, 210, 210, 210, 212, 210, 213, 194, 194, 194, 194, 214, 214, 214, 215, 216, 216, 216, 216, 216, 216, 216, 217, 216, 216, 216, 218, - 216, 219, 216, 219, 216, 220, 9, 9, 9, 221, 26, 26, 26, 26, 26, 26, - 222, 222, 222, 222, 222, 222, 222, 222, 222, 223, 222, 222, 222, 222, 222, 222, - 224, 224, 224, 224, 224, 224, 224, 224, 225, 225, 225, 225, 225, 225, 226, 227, - 228, 228, 228, 228, 228, 228, 228, 229, 228, 230, 231, 231, 231, 231, 231, 231, - 18, 232, 165, 165, 165, 165, 165, 233, 224, 26, 234, 9, 235, 236, 237, 238, - 2, 2, 2, 2, 239, 240, 2, 2, 2, 2, 2, 241, 242, 243, 2, 244, - 2, 2, 2, 2, 2, 2, 2, 245, 14, 14, 246, 246, 14, 14, 14, 14, - 246, 246, 14, 247, 14, 14, 14, 246, 14, 14, 14, 14, 14, 14, 248, 14, - 248, 14, 249, 250, 14, 14, 251, 252, 0, 253, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 254, 0, 255, 256, 0, 257, 2, 258, 0, 0, 0, 0, - 259, 26, 9, 9, 9, 9, 260, 26, 0, 0, 0, 0, 261, 262, 4, 0, - 0, 263, 0, 0, 2, 2, 2, 2, 2, 264, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 265, 26, 26, 0, 266, 26, 26, 0, 0, 0, 0, - 267, 267, 267, 267, 267, 267, 267, 267, 0, 0, 0, 0, 0, 0, 268, 0, - 0, 0, 269, 0, 0, 0, 0, 0, 270, 270, 270, 270, 270, 270, 270, 270, - 270, 270, 270, 270, 2, 2, 2, 2, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 271, 272, 165, 165, 165, 165, 166, 167, 273, 273, - 273, 273, 273, 273, 273, 274, 275, 274, 170, 170, 172, 26, 172, 172, 172, 172, - 172, 172, 172, 172, 18, 18, 18, 18, 0, 0, 0, 276, 26, 26, 26, 26, - 277, 277, 277, 278, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 279, 26, - 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 280, 26, 26, 26, 0, 0, - 281, 0, 0, 0, 282, 283, 0, 284, 285, 286, 286, 286, 286, 286, 286, 286, - 286, 286, 287, 288, 289, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 291, - 292, 293, 293, 293, 293, 293, 294, 169, 169, 295, 0, 0, 293, 293, 293, 293, - 0, 0, 0, 0, 276, 296, 290, 290, 169, 169, 169, 295, 0, 0, 0, 0, - 0, 0, 0, 0, 169, 169, 169, 297, 0, 0, 290, 290, 290, 290, 290, 298, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 0, 0, 0, 0, 0, - 299, 299, 299, 299, 299, 299, 299, 299, 299, 300, 299, 299, 299, 299, 299, 299, - 301, 26, 302, 302, 302, 302, 302, 302, 303, 303, 303, 303, 303, 303, 303, 303, - 303, 303, 303, 303, 303, 304, 26, 26, 18, 18, 18, 18, 305, 305, 305, 305, - 305, 305, 305, 305, 305, 305, 305, 26, 0, 0, 0, 0, 306, 2, 2, 2, - 2, 307, 2, 2, 2, 2, 2, 2, 2, 308, 309, 258, 26, 26, 310, 2, - 311, 311, 311, 311, 311, 312, 0, 265, 313, 313, 313, 313, 313, 313, 313, 26, - 314, 314, 314, 314, 314, 314, 314, 314, 315, 316, 314, 317, 53, 53, 53, 53, - 318, 318, 318, 318, 318, 319, 320, 320, 320, 320, 321, 322, 169, 169, 169, 323, - 324, 324, 324, 324, 324, 324, 324, 324, 324, 325, 324, 326, 164, 164, 164, 327, - 328, 328, 328, 328, 328, 328, 329, 26, 328, 330, 328, 331, 164, 164, 164, 164, - 332, 332, 332, 332, 332, 332, 332, 332, 333, 26, 26, 334, 335, 335, 336, 26, - 337, 337, 337, 26, 172, 172, 2, 2, 2, 2, 2, 338, 339, 340, 176, 176, - 176, 176, 176, 176, 176, 176, 176, 176, 335, 335, 335, 335, 335, 341, 335, 342, - 169, 169, 169, 169, 343, 26, 169, 169, 295, 344, 169, 169, 169, 169, 169, 343, - 26, 26, 26, 26, 26, 26, 26, 26, 277, 277, 277, 277, 277, 280, 277, 277, - 277, 277, 277, 345, 26, 26, 26, 26, 346, 26, 347, 348, 25, 25, 349, 350, - 351, 25, 31, 31, 31, 31, 31, 31, 352, 26, 353, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 354, 31, 31, 355, 31, 31, 31, 31, 31, - 31, 356, 26, 26, 26, 26, 31, 31, 9, 9, 0, 265, 9, 357, 0, 0, - 0, 0, 358, 0, 257, 359, 360, 31, 31, 31, 31, 31, 31, 31, 31, 361, - 362, 0, 0, 0, 1, 2, 2, 3, 1, 2, 2, 3, 363, 290, 289, 290, - 290, 290, 290, 364, 169, 169, 169, 295, 365, 365, 365, 366, 257, 257, 26, 367, - 368, 369, 368, 368, 370, 368, 368, 371, 368, 372, 368, 372, 26, 26, 26, 26, - 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 373, - 374, 0, 0, 0, 0, 0, 375, 0, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 252, 0, 376, 377, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 378, - 379, 379, 379, 380, 381, 381, 381, 381, 381, 381, 382, 26, 383, 0, 0, 359, - 384, 384, 384, 384, 385, 386, 387, 387, 387, 388, 389, 389, 389, 389, 389, 390, - 391, 391, 391, 392, 393, 393, 393, 393, 394, 393, 395, 26, 26, 26, 26, 26, - 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 397, 397, 397, 397, 397, 397, - 398, 398, 398, 399, 398, 400, 401, 401, 401, 401, 402, 401, 401, 401, 401, 402, - 403, 403, 403, 403, 403, 26, 404, 404, 404, 404, 404, 404, 405, 406, 407, 408, - 407, 408, 409, 407, 410, 407, 410, 411, 412, 412, 412, 412, 412, 412, 413, 26, - 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 415, 26, - 414, 414, 416, 26, 414, 26, 26, 26, 417, 2, 2, 2, 2, 2, 418, 419, - 420, 421, 422, 422, 422, 422, 423, 424, 425, 425, 426, 425, 427, 427, 427, 427, - 428, 428, 428, 429, 430, 428, 26, 26, 26, 26, 26, 26, 431, 431, 432, 433, - 434, 434, 434, 435, 436, 436, 436, 437, 438, 438, 438, 438, 439, 439, 439, 440, - 439, 439, 441, 439, 439, 439, 439, 439, 442, 443, 444, 445, 446, 446, 447, 448, - 446, 449, 446, 449, 450, 450, 450, 450, 451, 451, 451, 451, 26, 26, 26, 26, - 452, 452, 452, 452, 453, 454, 453, 26, 455, 455, 455, 455, 455, 455, 456, 457, - 458, 458, 459, 458, 460, 460, 461, 460, 462, 462, 463, 464, 26, 465, 26, 26, - 466, 466, 466, 466, 466, 466, 466, 466, 466, 467, 26, 26, 26, 26, 26, 26, - 468, 468, 468, 468, 468, 468, 469, 26, 468, 468, 468, 468, 468, 468, 469, 470, - 471, 471, 471, 471, 471, 26, 471, 472, 473, 473, 473, 473, 474, 475, 473, 473, - 474, 476, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 31, 31, 31, 50, - 477, 477, 477, 477, 477, 478, 479, 26, 480, 26, 26, 26, 26, 26, 26, 481, - 482, 482, 482, 482, 482, 26, 483, 483, 483, 483, 483, 484, 26, 26, 485, 485, - 485, 486, 26, 26, 26, 26, 487, 487, 487, 488, 26, 26, 489, 489, 490, 26, - 491, 491, 491, 491, 491, 491, 491, 491, 491, 492, 493, 491, 491, 491, 492, 494, - 495, 495, 495, 495, 495, 495, 495, 495, 496, 497, 498, 498, 498, 499, 498, 500, - 501, 501, 501, 501, 501, 501, 502, 501, 501, 26, 503, 503, 503, 503, 504, 26, - 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 506, 137, 507, 26, - 508, 508, 509, 508, 508, 508, 508, 508, 510, 26, 26, 26, 26, 26, 26, 26, - 511, 512, 513, 514, 513, 515, 516, 516, 516, 516, 516, 516, 516, 517, 516, 518, - 519, 520, 521, 522, 522, 523, 524, 525, 520, 526, 527, 528, 529, 530, 530, 26, - 531, 532, 531, 531, 531, 531, 533, 531, 534, 535, 533, 536, 537, 26, 26, 26, - 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 539, 540, 26, 26, 26, - 541, 541, 541, 541, 541, 541, 541, 541, 541, 26, 541, 542, 26, 26, 26, 26, - 543, 543, 543, 543, 543, 543, 544, 543, 543, 543, 543, 544, 26, 26, 26, 26, - 545, 545, 545, 545, 545, 545, 545, 545, 546, 26, 545, 547, 198, 548, 26, 26, - 549, 549, 549, 549, 549, 549, 549, 550, 549, 550, 164, 164, 551, 26, 26, 26, - 552, 552, 552, 553, 552, 554, 552, 552, 555, 26, 26, 26, 26, 26, 26, 26, - 556, 556, 556, 556, 556, 556, 556, 557, 26, 26, 26, 26, 558, 558, 558, 558, - 558, 558, 558, 558, 558, 558, 559, 560, 561, 562, 563, 564, 564, 564, 565, 566, - 561, 26, 564, 567, 26, 26, 26, 26, 26, 26, 26, 26, 568, 569, 568, 568, - 568, 568, 568, 569, 570, 26, 26, 26, 571, 571, 571, 571, 571, 571, 571, 571, - 571, 26, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 573, 26, 178, 178, - 574, 574, 574, 574, 574, 574, 574, 575, 53, 576, 26, 26, 26, 26, 26, 26, - 577, 577, 577, 577, 578, 26, 577, 578, 579, 580, 579, 579, 579, 579, 581, 579, - 582, 26, 579, 579, 579, 583, 584, 584, 584, 584, 585, 584, 584, 586, 587, 26, - 588, 589, 590, 590, 590, 590, 588, 591, 590, 26, 590, 592, 593, 594, 595, 595, - 595, 596, 597, 598, 595, 599, 26, 26, 26, 26, 26, 26, 600, 600, 600, 601, - 602, 602, 603, 602, 602, 602, 602, 604, 602, 602, 602, 605, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 606, 26, 108, 108, 108, 108, 108, 108, 607, 608, - 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 610, 26, 26, 26, 26, - 609, 609, 609, 609, 609, 611, 612, 26, 613, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 615, 26, - 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 617, 26, 616, 616, 616, 616, - 616, 616, 616, 616, 616, 616, 616, 618, 619, 619, 619, 619, 619, 619, 619, 619, - 620, 26, 26, 26, 26, 26, 26, 26, 621, 621, 621, 621, 621, 621, 621, 622, - 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 623, - 624, 624, 624, 625, 624, 626, 627, 627, 627, 627, 627, 627, 627, 627, 627, 628, - 627, 629, 630, 630, 630, 631, 631, 26, 632, 632, 632, 632, 632, 632, 632, 632, - 633, 26, 632, 634, 634, 632, 632, 635, 632, 632, 26, 26, 26, 26, 26, 26, - 636, 636, 636, 636, 636, 636, 636, 637, 638, 638, 638, 638, 638, 638, 638, 638, - 638, 638, 638, 639, 26, 26, 26, 26, 640, 640, 640, 640, 640, 640, 640, 640, - 640, 641, 640, 640, 640, 640, 640, 640, 640, 642, 640, 640, 26, 26, 26, 26, - 26, 26, 26, 26, 643, 26, 345, 26, 644, 644, 644, 644, 644, 644, 644, 644, - 644, 644, 644, 644, 644, 644, 644, 26, 645, 645, 645, 645, 645, 645, 645, 645, - 645, 645, 646, 26, 26, 26, 26, 647, 644, 648, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 649, 650, 651, 286, 286, 286, 286, 286, 286, 286, - 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 652, 26, 653, 26, - 26, 26, 654, 26, 655, 26, 656, 656, 656, 656, 656, 656, 656, 656, 656, 656, - 656, 656, 656, 656, 656, 656, 656, 657, 658, 658, 658, 658, 658, 658, 658, 658, - 658, 658, 658, 658, 658, 659, 658, 660, 658, 661, 658, 662, 359, 26, 26, 26, - 0, 0, 0, 0, 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, 359, 26, - 9, 9, 9, 9, 9, 663, 9, 9, 221, 26, 0, 0, 0, 0, 0, 0, - 359, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 276, 26, - 0, 0, 0, 0, 257, 362, 0, 0, 0, 0, 0, 0, 664, 665, 0, 666, - 667, 668, 0, 0, 0, 669, 0, 0, 0, 0, 0, 0, 0, 266, 26, 26, - 246, 26, 26, 26, 26, 26, 26, 26, 0, 0, 359, 26, 0, 0, 359, 26, - 0, 0, 257, 26, 0, 0, 0, 259, 0, 0, 254, 0, 0, 0, 0, 0, - 0, 0, 0, 254, 670, 671, 0, 672, 673, 0, 0, 0, 0, 0, 0, 0, - 269, 674, 254, 254, 0, 0, 0, 675, 676, 677, 678, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 276, 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, 0, - 679, 679, 679, 679, 679, 679, 679, 679, 679, 680, 26, 681, 682, 679, 26, 26, - 2, 2, 2, 346, 683, 419, 26, 26, 684, 270, 270, 685, 686, 687, 18, 18, - 18, 18, 18, 18, 18, 688, 26, 26, 26, 689, 26, 26, 26, 26, 26, 26, - 690, 690, 690, 690, 690, 691, 690, 692, 690, 693, 26, 26, 26, 26, 26, 26, - 26, 26, 694, 694, 694, 695, 26, 26, 696, 696, 696, 696, 696, 696, 696, 697, - 26, 26, 698, 698, 698, 698, 698, 699, 26, 26, 700, 700, 700, 700, 700, 701, - 26, 26, 26, 26, 172, 702, 170, 172, 703, 703, 703, 703, 703, 703, 703, 703, - 704, 703, 705, 26, 26, 26, 26, 26, 706, 706, 706, 706, 706, 706, 706, 706, - 706, 707, 706, 708, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 362, 0, - 0, 0, 0, 0, 0, 0, 376, 26, 362, 0, 0, 0, 0, 0, 0, 276, - 709, 31, 31, 31, 710, 711, 712, 713, 714, 715, 710, 716, 710, 712, 712, 717, - 31, 718, 31, 719, 720, 718, 31, 719, 26, 26, 26, 26, 26, 26, 721, 26, - 0, 0, 0, 0, 0, 359, 0, 0, 0, 0, 359, 26, 0, 257, 362, 0, - 362, 0, 362, 0, 0, 0, 276, 26, 0, 0, 0, 0, 0, 276, 26, 26, - 26, 26, 26, 26, 722, 0, 0, 0, 723, 26, 0, 0, 0, 0, 0, 359, - 0, 259, 265, 26, 276, 26, 26, 26, 0, 0, 0, 724, 0, 376, 0, 376, - 0, 0, 0, 0, 0, 0, 257, 725, 0, 0, 0, 265, 0, 359, 259, 26, - 0, 359, 0, 0, 0, 0, 0, 0, 0, 26, 0, 265, 0, 0, 0, 0, - 0, 26, 0, 0, 0, 276, 0, 359, 265, 26, 26, 26, 26, 26, 26, 26, - 0, 0, 359, 26, 0, 276, 0, 376, 0, 726, 0, 0, 0, 0, 0, 0, - 257, 722, 0, 727, 0, 265, 0, 259, 0, 0, 358, 0, 0, 0, 0, 0, - 277, 277, 277, 277, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 345, - 277, 277, 277, 280, 277, 277, 277, 277, 277, 277, 277, 277, 345, 26, 277, 277, - 277, 277, 277, 277, 728, 26, 277, 277, 277, 277, 277, 280, 26, 26, 26, 26, - 277, 729, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 26, 26, - 730, 26, 26, 26, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 0, 0, + 216, 219, 216, 219, 216, 220, 9, 9, 9, 9, 9, 221, 9, 222, 26, 26, + 223, 223, 223, 223, 223, 223, 223, 223, 223, 224, 223, 223, 223, 223, 223, 223, + 225, 225, 225, 225, 225, 225, 225, 225, 226, 226, 226, 226, 226, 226, 227, 228, + 229, 229, 229, 229, 229, 229, 229, 230, 229, 231, 232, 232, 232, 232, 232, 232, + 18, 233, 165, 165, 165, 165, 165, 234, 225, 26, 235, 9, 236, 237, 238, 239, + 2, 2, 2, 2, 240, 241, 2, 2, 2, 2, 2, 242, 243, 244, 2, 245, + 2, 2, 2, 2, 2, 2, 2, 246, 9, 9, 9, 9, 9, 9, 9, 9, + 14, 14, 247, 247, 14, 14, 14, 14, 247, 247, 14, 248, 14, 14, 14, 247, + 14, 14, 14, 14, 14, 14, 249, 14, 249, 14, 250, 251, 14, 14, 252, 253, + 0, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 256, 257, + 0, 258, 2, 259, 0, 0, 0, 0, 260, 26, 9, 9, 9, 9, 261, 26, + 0, 0, 0, 0, 262, 263, 4, 0, 0, 264, 0, 0, 2, 2, 2, 2, + 2, 265, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 260, 26, 26, 0, 266, 26, 26, 0, 0, 0, 0, + 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, + 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 2, 2, 2, 2, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 270, 271, + 165, 165, 165, 165, 166, 167, 272, 272, 272, 272, 272, 272, 272, 273, 274, 273, + 170, 170, 172, 26, 172, 172, 172, 172, 172, 172, 172, 172, 18, 18, 18, 18, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 275, 26, 26, 26, 26, + 276, 276, 276, 277, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 278, 26, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 279, 26, 26, 26, 0, 0, + 280, 0, 0, 0, 281, 282, 0, 283, 284, 285, 285, 285, 285, 285, 285, 285, + 285, 285, 286, 287, 288, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 290, + 291, 292, 292, 292, 292, 292, 293, 169, 169, 169, 169, 169, 169, 169, 169, 169, + 169, 294, 0, 0, 292, 292, 292, 292, 0, 0, 0, 0, 275, 295, 289, 289, + 169, 169, 169, 294, 0, 0, 0, 0, 0, 0, 0, 0, 169, 169, 169, 296, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 289, 289, 289, 289, 289, 297, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 0, 0, 0, 0, 0, + 276, 276, 276, 276, 276, 276, 276, 276, 0, 0, 0, 0, 0, 0, 0, 0, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 299, 298, 298, 298, 298, 298, 298, 300, 26, 301, 301, 301, 301, 301, 301, + 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, + 302, 302, 302, 302, 302, 303, 26, 26, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 26, + 0, 0, 0, 0, 305, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 306, 2, 2, 2, 2, 2, 2, 2, 2, 2, 259, 26, 26, 307, 2, + 308, 308, 308, 308, 308, 309, 0, 260, 310, 310, 310, 310, 310, 310, 310, 26, + 311, 311, 311, 311, 311, 311, 311, 311, 312, 313, 311, 314, 52, 52, 52, 52, + 315, 315, 315, 315, 315, 316, 317, 317, 317, 317, 318, 319, 169, 169, 169, 320, + 321, 321, 321, 321, 321, 321, 321, 321, 321, 322, 321, 323, 164, 164, 164, 324, + 325, 325, 325, 325, 325, 325, 326, 26, 325, 327, 325, 328, 164, 164, 164, 164, + 329, 329, 329, 329, 329, 329, 329, 329, 330, 26, 26, 331, 332, 332, 333, 26, + 334, 334, 334, 26, 172, 172, 2, 2, 2, 2, 2, 335, 336, 337, 176, 176, + 176, 176, 176, 176, 176, 176, 176, 176, 332, 332, 332, 332, 332, 338, 332, 339, + 169, 169, 169, 169, 340, 26, 169, 169, 294, 341, 169, 169, 169, 169, 169, 340, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 279, 276, 276, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 342, 26, 26, 26, 26, + 343, 26, 344, 345, 25, 25, 346, 347, 348, 25, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 349, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 26, 26, 26, 26, 31, 31, + 9, 9, 0, 260, 9, 350, 0, 0, 0, 0, 351, 0, 258, 352, 353, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 354, + 355, 0, 0, 0, 1, 2, 2, 3, 1, 2, 2, 3, 356, 289, 288, 289, + 289, 289, 289, 357, 169, 169, 169, 294, 358, 358, 358, 359, 258, 258, 26, 360, + 361, 362, 361, 361, 363, 361, 361, 364, 361, 365, 361, 365, 26, 26, 26, 26, + 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 366, + 367, 0, 0, 0, 0, 0, 368, 0, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 253, 0, 369, 370, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 371, + 372, 372, 372, 373, 374, 374, 374, 374, 374, 374, 375, 26, 376, 0, 0, 352, + 377, 377, 377, 377, 378, 379, 380, 380, 380, 381, 382, 382, 382, 382, 382, 383, + 384, 384, 384, 385, 386, 386, 386, 386, 387, 386, 388, 26, 26, 26, 26, 26, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 390, 390, 390, 390, 390, 390, + 391, 391, 391, 392, 391, 393, 394, 394, 394, 394, 395, 394, 394, 394, 394, 395, + 396, 396, 396, 396, 396, 26, 397, 397, 397, 397, 397, 397, 398, 399, 400, 401, + 400, 401, 402, 400, 403, 400, 403, 404, 405, 405, 405, 405, 405, 405, 406, 26, + 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, + 407, 407, 407, 407, 407, 407, 408, 26, 407, 407, 409, 26, 407, 26, 26, 26, + 410, 2, 2, 2, 2, 2, 411, 412, 26, 26, 26, 26, 26, 26, 26, 26, + 413, 414, 415, 415, 415, 415, 416, 417, 418, 418, 419, 418, 420, 420, 420, 420, + 421, 421, 421, 422, 423, 421, 26, 26, 26, 26, 26, 26, 424, 424, 425, 426, + 427, 427, 427, 428, 429, 429, 429, 430, 431, 431, 431, 432, 26, 26, 26, 26, + 433, 433, 433, 433, 434, 434, 434, 435, 434, 434, 436, 434, 434, 434, 434, 434, + 437, 438, 439, 440, 441, 441, 442, 443, 441, 444, 441, 444, 445, 445, 445, 445, + 446, 446, 446, 446, 26, 26, 26, 26, 447, 447, 447, 447, 448, 449, 448, 26, + 450, 450, 450, 450, 450, 450, 451, 452, 453, 453, 454, 453, 455, 455, 456, 455, + 457, 457, 458, 459, 26, 460, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 461, 461, 461, 461, 461, 461, 461, 461, 461, 462, 26, 26, 26, 26, 26, 26, + 463, 463, 463, 463, 463, 463, 464, 26, 463, 463, 463, 463, 463, 463, 464, 465, + 466, 466, 466, 466, 466, 26, 466, 467, 468, 468, 468, 468, 469, 470, 468, 468, + 469, 471, 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, 31, 31, 31, 472, + 473, 473, 473, 473, 473, 474, 475, 26, 476, 26, 31, 477, 26, 26, 26, 476, + 478, 478, 478, 478, 478, 26, 479, 479, 479, 479, 479, 480, 26, 26, 481, 481, + 481, 482, 26, 26, 26, 26, 483, 483, 483, 484, 26, 26, 485, 485, 486, 26, + 487, 487, 487, 487, 487, 487, 487, 487, 487, 488, 489, 487, 487, 487, 488, 490, + 491, 491, 491, 491, 491, 491, 491, 491, 492, 493, 494, 494, 494, 495, 494, 496, + 497, 497, 497, 497, 497, 497, 498, 497, 497, 26, 499, 499, 499, 499, 500, 26, + 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 502, 137, 503, 26, + 504, 504, 505, 504, 504, 504, 504, 504, 506, 26, 26, 26, 26, 26, 26, 26, + 507, 508, 509, 510, 509, 511, 512, 512, 512, 512, 512, 512, 512, 513, 512, 514, + 515, 516, 517, 518, 518, 519, 520, 521, 516, 522, 523, 524, 525, 526, 526, 26, + 527, 528, 527, 527, 527, 527, 529, 527, 530, 531, 529, 532, 533, 26, 26, 26, + 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 535, 536, 26, 26, 26, + 537, 537, 537, 537, 537, 537, 537, 537, 537, 26, 537, 538, 26, 26, 26, 26, + 539, 539, 539, 539, 539, 539, 540, 539, 539, 539, 539, 540, 26, 26, 26, 26, + 541, 541, 541, 541, 541, 541, 541, 541, 542, 26, 541, 543, 198, 544, 26, 26, + 545, 545, 545, 545, 545, 545, 545, 546, 545, 546, 164, 164, 547, 26, 26, 26, + 548, 548, 548, 549, 548, 550, 548, 548, 551, 26, 26, 26, 26, 26, 26, 26, + 552, 552, 552, 552, 552, 552, 552, 553, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 555, 556, + 557, 558, 559, 560, 560, 560, 561, 562, 557, 26, 560, 563, 26, 26, 26, 26, + 26, 26, 26, 26, 564, 565, 564, 564, 564, 564, 564, 565, 566, 26, 26, 26, + 567, 567, 567, 567, 567, 567, 567, 567, 567, 26, 568, 568, 568, 568, 568, 568, + 568, 568, 568, 568, 569, 26, 178, 178, 570, 570, 570, 570, 570, 570, 570, 571, + 52, 572, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 501, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 573, 573, 573, 573, 574, 26, 573, 574, + 575, 576, 575, 575, 575, 575, 577, 575, 578, 26, 575, 575, 575, 579, 580, 580, + 580, 580, 581, 580, 580, 582, 583, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 584, 585, 586, 586, 586, 586, 584, 587, 586, 26, 586, 588, 589, 590, 591, 591, + 591, 592, 593, 594, 591, 595, 596, 596, 596, 596, 596, 597, 596, 598, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 599, 599, 599, 600, + 601, 601, 602, 601, 601, 601, 601, 603, 601, 601, 601, 604, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 605, 26, 107, 107, 107, 107, 107, 107, 606, 607, + 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, + 608, 608, 608, 609, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 610, 611, 26, + 608, 608, 608, 608, 608, 608, 608, 608, 612, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 614, 26, + 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, + 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 616, 26, 615, 615, 615, 615, + 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 617, + 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, + 618, 618, 618, 618, 618, 618, 618, 618, 619, 26, 26, 26, 26, 26, 26, 26, + 620, 620, 620, 620, 620, 620, 620, 621, 26, 26, 26, 26, 26, 26, 26, 26, + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 622, 623, 623, 623, 624, 623, 625, 626, 626, + 626, 626, 626, 626, 626, 626, 626, 627, 626, 628, 629, 629, 629, 630, 630, 26, + 631, 631, 631, 631, 631, 631, 631, 631, 632, 26, 631, 633, 633, 631, 631, 634, + 631, 631, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 635, 635, 635, 635, 635, 635, 635, 636, + 26, 26, 26, 26, 26, 26, 26, 26, 637, 637, 637, 637, 637, 637, 637, 637, + 637, 637, 637, 638, 639, 639, 639, 640, 639, 639, 641, 26, 26, 26, 26, 26, + 642, 642, 642, 642, 642, 642, 642, 642, 642, 643, 642, 642, 642, 642, 642, 642, + 642, 644, 642, 642, 26, 26, 26, 26, 26, 26, 26, 26, 645, 26, 646, 26, + 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, + 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, + 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 649, 26, 26, 26, 26, 650, + 647, 647, 647, 651, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 652, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 653, 654, + 655, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, + 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, + 285, 285, 285, 285, 656, 26, 657, 26, 26, 26, 658, 26, 659, 26, 660, 660, + 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, + 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 661, + 662, 662, 662, 662, 662, 662, 662, 662, 662, 662, 662, 662, 662, 663, 662, 664, + 662, 665, 662, 666, 352, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 369, + 0, 0, 0, 0, 0, 0, 352, 667, 0, 0, 668, 26, 0, 0, 668, 26, + 9, 9, 9, 9, 9, 221, 9, 9, 669, 26, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 352, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 275, 26, + 0, 0, 0, 0, 258, 355, 0, 0, 0, 0, 0, 0, 670, 671, 0, 672, + 673, 674, 0, 0, 0, 675, 0, 0, 0, 0, 0, 0, 0, 266, 26, 26, + 14, 14, 14, 14, 14, 14, 14, 14, 247, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 352, 26, 0, 0, 352, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258, 26, 0, 0, 0, 668, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 676, 677, 0, 678, 679, 0, 0, 0, 0, 0, 0, 0, + 680, 681, 255, 255, 0, 0, 0, 682, 683, 667, 684, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 275, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, 0, + 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, + 685, 686, 26, 687, 688, 685, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 2, 2, 2, 343, 689, 412, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 690, 269, 269, 691, 692, 693, 18, 18, 18, 18, 18, 18, 18, 694, 26, 26, + 26, 695, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 696, 696, 696, 696, 696, 697, 696, 698, 696, 699, 26, 26, 26, 26, 26, 26, + 26, 26, 700, 700, 700, 701, 26, 26, 702, 702, 702, 702, 702, 702, 702, 703, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 704, 704, 704, 704, 704, 705, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 706, 706, 706, 706, 706, 707, + 26, 26, 26, 26, 26, 26, 26, 26, 708, 708, 708, 709, 708, 708, 710, 711, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 172, 712, 170, 172, + 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, + 713, 713, 713, 713, 713, 713, 713, 713, 714, 713, 715, 26, 26, 26, 26, 26, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 717, 716, 718, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 355, 0, + 0, 0, 0, 0, 0, 0, 369, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 355, 0, 0, 0, 0, 0, 0, 275, 26, 26, 26, 26, 26, 26, 26, 26, + 719, 31, 31, 31, 720, 721, 722, 723, 724, 725, 720, 726, 720, 722, 722, 727, + 31, 728, 31, 729, 730, 728, 31, 729, 26, 26, 26, 26, 26, 26, 731, 26, + 0, 0, 0, 0, 0, 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 352, 26, 0, 258, 355, 0, 355, 0, 355, 0, 0, 0, 275, 26, + 0, 0, 0, 0, 0, 275, 26, 26, 26, 26, 26, 26, 732, 0, 0, 0, + 733, 26, 0, 0, 0, 0, 0, 352, 0, 668, 260, 26, 275, 26, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 734, 0, 369, 0, 369, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 260, 0, 352, 668, 26, + 0, 352, 0, 0, 0, 0, 0, 0, 0, 26, 0, 260, 0, 0, 0, 0, + 0, 26, 0, 0, 0, 275, 0, 352, 260, 26, 0, 668, 26, 26, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0, 275, 0, 369, + 0, 735, 0, 0, 0, 0, 0, 0, 258, 736, 0, 737, 0, 367, 0, 668, + 0, 0, 351, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 266, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 26, 26, 26, 26, + 276, 276, 276, 279, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, + 276, 276, 276, 276, 276, 279, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 738, 26, 276, 276, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 279, 26, 26, 26, 26, + 276, 276, 276, 279, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 739, 276, 276, 276, 276, 276, 276, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 342, + 740, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0, @@ -2840,8 +2688,7 @@ _hb_ucd_u16[10400] = 789, 928, 792, 95, 796, 797, 798, 800, 96, 929, 802, 804, 806, 97, 98, 807, 930, 99, 931, 932, 933, 814, 100, 816, 817, 818, 819, 820, 821, 935, 0, 0, }; -static const int16_t -_hb_ucd_i16[196] = +static const int16_t _hb_ucd_i16[196]= { 0, 0, 0, 0, 1, -1, 0, 0, 2, 0, -2, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 16, 0, 0, 0, -16, 0, 0, 1, -1, @@ -2858,47 +2705,42 @@ _hb_ucd_i16[196] = -1, 0, 1, -1, }; -static inline uint_fast8_t -_hb_ucd_gc (unsigned u) +static inline uint8_t _hb_ucd_gc (unsigned u) { - return u<1114110u?_hb_ucd_u8[6472+(((_hb_ucd_u8[816+(((_hb_ucd_u16[((_hb_ucd_u8[272+(((_hb_ucd_u8[u>>1>>3>>4>>4])<<4)+((u>>1>>3>>4)&15u))])<<4)+((u>>1>>3)&15u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; + return u<1114110 ? _hb_ucd_u8[7920u+((_hb_ucd_u8[2176u+((_hb_ucd_u16[((_hb_ucd_u8[((((((u)>>1))>>3))>>5)])<<5)+((((((u)>>1))>>3))&31)])<<3)+((((u)>>1))&7)])<<1)+((u)&1)] : 2; } -static inline uint_fast8_t -_hb_ucd_ccc (unsigned u) +static inline uint8_t _hb_ucd_ccc (unsigned u) { - return u<125259u?_hb_ucd_u8[8504+(((_hb_ucd_u8[7936+(((_hb_ucd_u8[7460+(((_hb_ucd_u8[7100+(((_hb_ucd_u8[6854+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; + return u<125259 ? _hb_ucd_u8[10388u+((_hb_ucd_u8[9284u+((_hb_ucd_u8[8548u+((_hb_ucd_u8[8302u+((((((u)>>2))>>3))>>4)])<<4)+((((((u)>>2))>>3))&15)])<<3)+((((u)>>2))&7)])<<2)+((u)&3)] : 0; } -static inline unsigned -_hb_ucd_b4 (const uint8_t* a, unsigned i) +static inline uint8_t _hb_ucd_b4 (const uint8_t* a, unsigned i) { - return (a[i>>1]>>((i&1u)<<2))&15u; + return (a[i>>1]>>((i&1)<<2))&15; } -static inline int_fast16_t -_hb_ucd_bmg (unsigned u) +static inline int16_t _hb_ucd_bmg (unsigned u) { - return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9252+(((_hb_ucd_u8[9132+(((_hb_ucd_b4(9004+_hb_ucd_u8,u>>2>>3>>3))<<3)+((u>>2>>3)&7u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u)]:0; + return u<65380 ? _hb_ucd_i16[((_hb_ucd_u8[11140u+((_hb_ucd_u8[11020u+((_hb_ucd_b4(_hb_ucd_u8+10892u,((((((u)>>2))>>3))>>3)))<<3)+((((((u)>>2))>>3))&7)])<<3)+((((u)>>2))&7)])<<2)+((u)&3)] : 0; } -static inline uint_fast8_t -_hb_ucd_sc (unsigned u) +static inline uint8_t _hb_ucd_sc (unsigned u) { - return u<918000u?_hb_ucd_u8[10486+(((_hb_ucd_u16[3744+(((_hb_ucd_u16[2624+(((_hb_ucd_u8[9588+(u>>3>>3>>4)])<<4)+((u>>3>>3)&15u))])<<3)+((u>>3)&7u))])<<3)+((u)&7u))]:2; + return u<918000 ? _hb_ucd_u8[12662u+((_hb_ucd_u16[3328u+((_hb_ucd_u8[11926u+((_hb_ucd_u8[11476u+((((((u)>>3))>>4))>>4)])<<4)+((((((u)>>3))>>4))&15)])<<4)+((((u)>>3))&15)])<<3)+((u)&7)] : 2; } -static inline uint_fast16_t -_hb_ucd_dm (unsigned u) +static inline uint16_t _hb_ucd_dm (unsigned u) { - return u<195102u?_hb_ucd_u16[6976+(((_hb_ucd_u8[16716+(((_hb_ucd_u8[16334+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; + return u<195102 ? _hb_ucd_u16[7408u+((_hb_ucd_u8[18972u+((_hb_ucd_u8[18590u+((((u)>>4))>>5)])<<5)+((((u)>>4))&31)])<<4)+((u)&15)] : 0; } #elif !defined(HB_NO_UCD_UNASSIGNED) -static const uint8_t -_hb_ucd_u8[17524] = +#include + +static const uint8_t _hb_ucd_u8[14800]= { 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 5, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 5, 17, 15, 18, 19, 20, 21, 22, 23, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 24, 25, 26, 5, 27, 28, - 5, 29, 30, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 5, 29, 5, 30, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, @@ -2929,23 +2771,23 @@ _hb_ucd_u8[17524] = 17, 17, 17,103, 17, 17,104,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,105,100,100,100,100,100,100, 17, 17,106,107,100,108,109,110, - 17, 17, 17, 17, 17, 17, 17,111, 17, 17, 17, 17,112,113,100,100, - 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,114, - 17,115,116,100,100,100,100,100,100,100,100,100,117,100,100,100, - 100,100,100,100,100,100,100,100,100,100,100,100,118, 39,119,120, - 121,122,123,124,125,126,127,128, 39, 39,129,100,100,100,100,130, - 131,132,133,100,134,135,100,136,137,138,100,100,139,140,141,100, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,111,112,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,113, + 17,114,115,100,100,100,100,100,100,100,100,100,116,100,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,117, 39,118,119, + 120,121,122,123,124,125,126,127, 39, 39,128,100,100,100,100,129, + 130,131,132,100,133,134,135,136,137,138,100,100,139,140,141,100, 142,143,144,145, 39, 39,146,147,148, 39,149,150,100,100,100,100, 17, 17, 17, 17, 17, 17,151, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17,152,153, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,154, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,155, 17, 17,156,100, - 100,100,100,100,100,100,100,100, 17, 17,157,100,100,100,100,100, - 17, 17, 17,158, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17,159,100,100,100,100,100,100,100,100,100,100,100,100, - 160,161,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 17, 17, 17, 17, 17, 17, 17, 17,152, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,153, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,154, 17, 17,155,100, + 100,100,100,100,100,100,100,100, 17, 17,156,100,100,100,100,100, + 17, 17, 17,157, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17,158,100,100,100,100,100,100,100,100,100,100,100, + 159,160,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,161, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,162, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,163, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, @@ -2959,409 +2801,415 @@ _hb_ucd_u8[17524] = 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, - 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, - 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, - 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, - 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 44, 45, 16, 10, - 44, 44, 41, 46, 11, 47, 47, 11, 34, 11, 11, 11, 11, 11, 11, 11, - 11, 48, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, - 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 49, 34, 32, 34, 11, - 32, 50, 43, 43, 51, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, - 48, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 47, 52, 2, 2, 2, - 16, 16, 16, 16, 53, 54, 55, 56, 57, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 58, 59, 60, 43, 59, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 62, - 36, 63, 64, 44, 44, 44, 44, 44, 65, 65, 65, 8, 9, 66, 2, 67, - 43, 43, 43, 43, 43, 60, 68, 2, 69, 36, 36, 36, 36, 70, 43, 43, - 7, 7, 7, 7, 7, 2, 2, 36, 71, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 72, 43, 43, 43, 73, 50, 43, 43, 74, 75, 76, 43, 43, 36, - 7, 7, 7, 7, 7, 36, 77, 78, 2, 2, 2, 2, 2, 2, 2, 79, - 70, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 80, 62, 36, - 36, 36, 36, 43, 43, 43, 43, 43, 71, 44, 44, 44, 44, 44, 44, 44, - 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 70, 43, 43, - 43, 43, 40, 21, 2, 81, 57, 20, 36, 36, 36, 43, 43, 75, 43, 43, - 43, 43, 75, 43, 75, 43, 43, 44, 2, 2, 2, 2, 2, 2, 2, 64, - 36, 36, 36, 36, 70, 43, 44, 64, 36, 36, 36, 36, 36, 61, 44, 44, - 36, 36, 36, 36, 82, 36, 36, 61, 65, 44, 44, 57, 43, 43, 43, 43, - 36, 36, 36, 36, 83, 43, 43, 43, 43, 84, 43, 43, 43, 43, 43, 43, - 43, 85, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 85, 71, 86, - 87, 43, 43, 43, 85, 86, 87, 86, 70, 43, 43, 43, 36, 36, 36, 36, - 36, 43, 2, 7, 7, 7, 7, 7, 88, 36, 36, 36, 36, 36, 36, 36, - 70, 86, 62, 36, 36, 36, 61, 62, 61, 62, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 61, 36, 36, 36, 61, 61, 44, 36, 36, 44, 71, 86, - 87, 43, 80, 89, 90, 89, 87, 61, 44, 44, 44, 89, 44, 44, 36, 62, - 36, 43, 44, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 56, 63, 80, - 57, 85, 62, 36, 36, 61, 44, 62, 61, 36, 62, 61, 36, 44, 80, 86, - 87, 80, 44, 57, 80, 57, 43, 44, 57, 44, 44, 44, 62, 36, 61, 61, - 44, 44, 44, 7, 7, 7, 7, 7, 43, 36, 70, 64, 44, 44, 44, 44, - 57, 85, 62, 36, 36, 36, 36, 62, 36, 62, 36, 36, 36, 36, 36, 36, - 61, 36, 62, 36, 36, 44, 71, 86, 87, 43, 43, 57, 85, 89, 87, 44, - 61, 44, 44, 44, 44, 44, 44, 44, 66, 44, 44, 44, 62, 43, 43, 43, - 57, 86, 62, 36, 36, 36, 61, 62, 61, 36, 62, 36, 36, 44, 71, 87, - 87, 43, 80, 89, 90, 89, 87, 44, 44, 44, 57, 85, 44, 44, 36, 62, - 78, 27, 27, 27, 44, 44, 44, 44, 44, 71, 62, 36, 36, 61, 44, 36, - 61, 36, 36, 44, 62, 61, 61, 36, 44, 62, 61, 44, 36, 61, 44, 36, - 36, 36, 36, 36, 36, 44, 44, 86, 85, 90, 44, 86, 90, 86, 87, 44, - 61, 44, 44, 89, 44, 44, 44, 44, 27, 91, 67, 67, 56, 92, 44, 44, - 85, 86, 71, 36, 36, 36, 61, 36, 61, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 44, 71, 43, 85, 86, 90, 43, 80, 43, 43, 44, - 44, 44, 57, 80, 36, 61, 62, 44, 44, 44, 44, 93, 27, 27, 27, 91, - 70, 86, 72, 36, 36, 36, 61, 36, 36, 36, 62, 36, 36, 44, 71, 87, - 86, 86, 90, 85, 90, 86, 43, 44, 44, 44, 89, 90, 44, 44, 62, 61, - 62, 94, 44, 44, 44, 44, 44, 44, 43, 86, 36, 36, 36, 36, 61, 36, - 36, 36, 36, 36, 36, 70, 71, 86, 87, 43, 80, 86, 90, 86, 87, 77, - 44, 44, 36, 94, 27, 27, 27, 95, 27, 27, 27, 27, 91, 36, 36, 36, - 57, 86, 62, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 36, 36, 36, - 36, 62, 36, 36, 36, 36, 62, 44, 36, 36, 36, 61, 44, 80, 44, 89, - 86, 43, 80, 80, 86, 86, 86, 86, 44, 86, 64, 44, 44, 44, 44, 44, - 62, 36, 36, 36, 36, 36, 36, 36, 70, 36, 43, 43, 43, 80, 44, 96, - 36, 36, 36, 75, 43, 43, 43, 60, 7, 7, 7, 7, 7, 2, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 62, 61, 61, 36, 36, 61, 36, 36, - 36, 36, 62, 62, 36, 36, 36, 36, 70, 36, 43, 43, 43, 43, 71, 44, - 36, 36, 61, 81, 43, 43, 43, 80, 7, 7, 7, 7, 7, 44, 36, 36, - 77, 67, 2, 2, 2, 2, 2, 2, 2, 97, 97, 67, 43, 67, 67, 67, - 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 50, 50, 50, 4, 4, 86, - 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, - 57, 43, 43, 43, 43, 43, 43, 85, 43, 43, 60, 43, 36, 36, 70, 43, - 43, 43, 43, 43, 57, 43, 43, 43, 43, 43, 43, 43, 43, 43, 80, 67, - 67, 67, 67, 76, 67, 67, 92, 67, 2, 2, 97, 67, 21, 64, 44, 44, - 36, 36, 36, 36, 36, 94, 87, 43, 85, 43, 43, 43, 87, 85, 87, 71, - 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 86, 43, 36, 36, 43, - 71, 86, 98, 94, 86, 86, 86, 36, 70, 43, 71, 36, 36, 36, 36, 36, - 36, 85, 87, 85, 86, 86, 87, 94, 7, 7, 7, 7, 7, 86, 87, 67, - 11, 11, 11, 48, 44, 44, 48, 44, 16, 16, 16, 16, 16, 53, 45, 16, - 36, 36, 36, 36, 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, - 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, 36, 36, 36, 36, - 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 57, 43, - 2, 2, 2, 2, 99, 27, 27, 27, 27, 27, 27, 27, 27, 27,100, 44, - 67, 67, 67, 67, 67, 44, 44, 44, 11, 11, 11, 44, 16, 16, 16, 44, - 101, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 72, - 102, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,103,104, 44, - 36, 36, 36, 36, 36, 63, 2,105,106, 36, 36, 36, 61, 44, 44, 44, - 36, 43, 85, 44, 44, 44, 44, 62, 36, 43,107, 64, 44, 44, 44, 44, - 36, 43, 44, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 61, 36, - 61, 43, 44, 44, 44, 44, 44, 44, 36, 36, 43, 87, 43, 43, 43, 86, - 86, 86, 86, 85, 87, 43, 43, 43, 43, 43, 2, 88, 2, 66, 70, 44, - 7, 7, 7, 7, 7, 44, 44, 44, 27, 27, 27, 27, 27, 44, 44, 44, - 2, 2, 2,108, 2, 59, 43, 84, 36, 83, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 61, 44, 44, 44, 36, 36, 70, 71, 36, 36, 36, 36, - 36, 36, 36, 36, 70, 61, 44, 44, 36, 36, 36, 44, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 36, 36, 61, 43, 85, 86, 87, 85, 86, 44, 44, - 86, 85, 86, 86, 87, 43, 44, 44, 92, 44, 2, 7, 7, 7, 7, 7, - 36, 36, 36, 36, 36, 36, 36, 44, 36, 36, 61, 44, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 36, 44, 44, 36, 36, 36, 36, 36, 44, 44, 44, - 7, 7, 7, 7, 7,100, 44, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 36, 36, 36, 70, 85, 87, 44, 2, 36, 36, 94, 85, 43, 43, 43, 80, - 85, 85, 87, 43, 43, 43, 85, 86, 86, 87, 43, 43, 43, 43, 80, 57, - 2, 2, 2, 88, 2, 2, 2, 44, 43, 43, 43, 43, 43, 43, 43,109, - 43, 43, 43, 43, 43, 43, 43, 80, 43, 43, 98, 36, 36, 36, 36, 36, - 36, 36, 85, 43, 43, 85, 85, 86, 86, 85, 98, 36, 36, 36, 61, 2, - 97, 67, 67, 67, 67, 50, 43, 43, 43, 43, 67, 67, 67, 67, 21, 2, - 43, 98, 36, 36, 36, 36, 36, 36, 94, 43, 43, 86, 43, 87, 43, 36, - 36, 36, 36, 85, 43, 86, 87, 87, 43, 86, 44, 44, 44, 44, 2, 2, - 36, 36, 86, 86, 86, 86, 43, 43, 43, 43, 86, 43, 44, 93, 2, 2, - 7, 7, 7, 7, 7, 44, 62, 36, 36, 36, 36, 36, 40, 40, 40, 2, - 16, 16, 16, 16, 34,110, 44, 44, 11, 11, 11, 11, 11, 47, 48, 11, - 2, 2, 2, 2, 44, 44, 44, 44, 43, 60, 43, 43, 43, 43, 43, 43, - 85, 43, 43, 43, 71, 36, 70, 36, 36, 36, 71, 94, 43, 61, 44, 44, - 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 45, 16, 16, - 16, 16, 16, 16, 45, 16, 16, 16, 16, 16, 16, 16, 16,111, 40, 40, - 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, - 16, 16, 16, 44, 11, 11, 11, 44, 16, 16, 16, 16, 48, 48, 48, 48, - 16, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, 16,112,112,112,112, - 16, 16,110, 16, 11, 11,113,114, 41, 16,110, 16, 11, 11,113, 41, - 16, 16, 44, 16, 11, 11,115, 41, 16, 16, 16, 16, 11, 11,116, 41, - 44, 16,110, 16, 11, 11,113,117,118,118,118,118,118,119, 65, 65, - 120,120,120, 2,121,122,121,122, 2, 2, 2, 2,123, 65, 65,124, - 2, 2, 2, 2,125,126, 2,127,128, 2,129,130, 2, 2, 2, 2, - 2, 9,128, 2, 2, 2, 2,131, 65, 65,132, 65, 65, 65, 65, 65, - 133, 44, 27, 27, 27, 8,129,134, 27, 27, 27, 27, 27, 8,129,104, - 40, 40, 40, 40, 40, 40, 81, 44, 20, 20, 20, 20, 20, 20, 20, 20, - 135, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43,136, 51, - 109, 51,109, 43, 43, 43, 43, 43, 80, 44, 44, 44, 44, 44, 44, 44, - 67,137, 67,138, 67, 34, 11, 16, 11, 32,138, 67, 49, 11, 11, 67, - 67, 67,137,137,137, 11, 11,139, 11, 11, 35, 36, 39, 67, 16, 11, - 8, 8, 49, 16, 16, 26, 67,140, 27, 27, 27, 27, 27, 27, 27, 27, - 105,105,105,105,105,105,105,105,105,141,142,105,143, 67, 44, 44, - 8, 8,144, 67, 67, 8, 67, 67,144, 26, 67,144, 67, 67, 67,144, - 67, 67, 67, 67, 67, 67, 67, 8, 67,144,144, 67, 67, 67, 67, 67, - 67, 67, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 67, 67, 67, 67, 4, 4, 67, 67, 8, 67, 67, 67,145,146, 67, 67, - 67, 67, 67, 67, 67, 67,144, 67, 67, 67, 67, 67, 67, 26, 8, 8, - 8, 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, - 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 92, 44, 44, - 27, 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, - 67, 67, 67, 26, 67, 67, 67, 67, 26, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 8, 8, 8, 8, 67, 67, 67, 67, 67, 67, 67, 26, - 67, 67, 67, 67, 4, 4, 4, 4, 4, 4, 4, 27, 27, 27, 27, 27, - 27, 27, 67, 67, 67, 67, 67, 67, 8, 8,129,147, 8, 8, 8, 8, - 8, 8, 8, 4, 4, 4, 4, 4, 8,129,148,148,148,148,148,148, - 148,148,148,148,147, 8, 8, 8, 8, 8, 8, 8, 4, 4, 8, 8, - 8, 8, 8, 8, 8, 8, 4, 8, 8, 8,144, 26, 8, 8,144, 67, - 67, 67, 44, 67, 67, 67, 67, 67, 67, 67, 67, 55, 67, 67, 67, 67, - 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 40, 11, - 32, 32,140, 67, 67,138, 34,149, 43, 32, 44, 44, 93, 2, 99, 2, - 16, 16, 16,150, 44, 44,150, 44, 36, 36, 36, 36, 44, 44, 44, 52, - 64, 44, 44, 44, 44, 44, 44, 57, 36, 36, 36, 61, 44, 44, 44, 44, - 36, 36, 36, 61, 36, 36, 36, 61, 2,121,121, 2,125,126,121, 2, - 2, 2, 2, 6, 2,108,121, 2,121, 4, 4, 4, 4, 2, 2, 88, - 2, 2, 2, 2, 2,120, 2, 2,108,151, 2, 2, 2, 2, 2, 2, - 67, 2,152,148,148,148,153, 44, 67, 67, 67, 67, 67, 55, 67, 67, - 67, 67, 44, 44, 44, 44, 44, 44, 67, 67, 67, 44, 44, 44, 44, 44, - 1, 2,154,155, 4, 4, 4, 4, 4, 67, 4, 4, 4, 4,156,157, - 158,105,105,105,105, 43, 43, 86,159, 40, 40, 67,105,160, 63, 67, - 36, 36, 36, 61, 57,161,162, 69, 36, 36, 36, 36, 36, 63, 40, 69, - 44, 44, 62, 36, 36, 36, 36, 36, 67, 27, 27, 67, 67, 67, 67, 67, - 67, 67, 67, 44, 44, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, 92, - 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, 27, - 163, 27, 27, 27, 27, 27, 27, 27, 36, 36, 83, 36, 36, 36, 36, 36, - 67, 67, 67, 92, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36,164, 2, - 7, 7, 7, 7, 7, 36, 44, 44, 32, 32, 32, 32, 32, 32, 32, 70, - 51,165, 43, 43, 43, 43, 43, 88, 32, 32, 32, 32, 32, 32, 40, 43, - 36, 36, 36,105,105,105,105,105, 43, 2, 2, 2, 44, 44, 44, 44, - 41, 41, 41,162, 40, 40, 40, 40, 41, 32, 32, 32, 32, 32, 32, 32, - 16, 32, 32, 32, 32, 32, 32, 32, 45, 16, 16, 16, 34, 34, 34, 32, - 32, 32, 32, 32, 42,166, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, + 16, 16, 36, 16, 16, 16, 16, 16, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 40, 40, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, + 39, 39, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, + 42, 42, 42, 42, 42, 42, 42, 42, 32, 32, 41, 32, 43, 44, 16, 10, + 43, 43, 40, 45, 11, 46, 46, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 47, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 48, 34, 32, 34, 11, + 32, 49, 42, 42, 50, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 47, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 46, 51, 2, 2, 2, + 16, 16, 16, 16, 52, 53, 54, 55, 56, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 57, 58, 59, 42, 58, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, 61, + 36, 62, 63, 43, 43, 43, 43, 43, 64, 64, 64, 8, 9, 65, 2, 66, + 42, 42, 42, 42, 42, 59, 67, 2, 68, 36, 36, 36, 36, 69, 42, 42, + 7, 7, 7, 7, 7, 2, 2, 36, 70, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 71, 42, 42, 42, 72, 49, 42, 42, 73, 74, 75, 42, 42, 36, + 7, 7, 7, 7, 7, 36, 76, 77, 2, 2, 2, 2, 2, 2, 2, 78, + 69, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 79, 61, 36, + 36, 36, 36, 42, 42, 42, 42, 42, 70, 43, 43, 43, 43, 43, 43, 43, + 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 69, 42, 42, + 42, 42, 39, 21, 2, 80, 56, 20, 36, 36, 36, 42, 42, 74, 42, 42, + 42, 42, 74, 42, 74, 42, 42, 43, 2, 2, 2, 2, 2, 2, 2, 63, + 36, 36, 36, 36, 69, 42, 43, 63, 36, 36, 36, 36, 36, 60, 43, 43, + 36, 36, 36, 36, 81, 36, 36, 36, 64, 43, 43, 56, 42, 42, 42, 42, + 36, 36, 36, 36, 82, 42, 42, 42, 42, 83, 42, 42, 42, 42, 42, 42, + 42, 84, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 84, 70, 85, + 86, 42, 42, 42, 84, 85, 86, 85, 69, 42, 42, 42, 36, 36, 36, 36, + 36, 42, 2, 7, 7, 7, 7, 7, 87, 36, 36, 36, 36, 36, 36, 36, + 69, 85, 61, 36, 36, 36, 60, 61, 60, 61, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 60, 36, 36, 36, 60, 60, 43, 36, 36, 43, 70, 85, + 86, 42, 79, 88, 89, 88, 86, 60, 43, 43, 43, 88, 43, 43, 36, 61, + 36, 42, 43, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 55, 62, 79, + 56, 84, 61, 36, 36, 60, 43, 61, 60, 36, 61, 60, 36, 43, 79, 85, + 86, 79, 43, 56, 79, 56, 42, 43, 56, 43, 43, 43, 61, 36, 60, 60, + 43, 43, 43, 7, 7, 7, 7, 7, 42, 36, 69, 63, 43, 43, 43, 43, + 56, 84, 61, 36, 36, 36, 36, 61, 36, 61, 36, 36, 36, 36, 36, 36, + 60, 36, 61, 36, 36, 43, 70, 85, 86, 42, 42, 56, 84, 88, 86, 43, + 60, 43, 43, 43, 43, 43, 43, 43, 65, 43, 43, 43, 61, 42, 42, 42, + 56, 85, 61, 36, 36, 36, 60, 61, 60, 36, 61, 36, 36, 43, 70, 86, + 86, 42, 79, 88, 89, 88, 86, 43, 43, 43, 56, 84, 43, 43, 36, 61, + 77, 27, 27, 27, 43, 43, 43, 43, 43, 70, 61, 36, 36, 60, 43, 36, + 60, 36, 36, 43, 61, 60, 60, 36, 43, 61, 60, 43, 36, 60, 43, 36, + 36, 36, 36, 36, 36, 43, 43, 85, 84, 89, 43, 85, 89, 85, 86, 43, + 60, 43, 43, 88, 43, 43, 43, 43, 27, 90, 66, 66, 55, 91, 43, 43, + 84, 85, 70, 36, 36, 36, 60, 36, 60, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 43, 70, 42, 84, 85, 89, 42, 79, 42, 42, 43, + 43, 43, 56, 79, 36, 60, 36, 43, 43, 43, 43, 92, 27, 27, 27, 90, + 69, 85, 71, 36, 36, 36, 60, 36, 36, 36, 61, 36, 36, 43, 70, 86, + 85, 85, 89, 84, 89, 85, 42, 43, 43, 43, 88, 89, 43, 43, 36, 60, + 61, 93, 43, 43, 43, 43, 43, 43, 42, 85, 36, 36, 36, 36, 60, 36, + 36, 36, 36, 36, 36, 69, 70, 85, 86, 42, 79, 85, 89, 85, 86, 76, + 43, 43, 36, 93, 27, 27, 27, 94, 27, 27, 27, 27, 90, 36, 36, 36, + 56, 85, 61, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, 36, 36, 36, + 36, 61, 36, 36, 36, 36, 61, 43, 36, 36, 36, 60, 43, 79, 43, 88, + 85, 42, 79, 79, 85, 85, 85, 85, 43, 85, 63, 43, 43, 43, 43, 43, + 61, 36, 36, 36, 36, 36, 36, 36, 69, 36, 42, 42, 42, 79, 43, 95, + 36, 36, 36, 74, 42, 42, 42, 59, 7, 7, 7, 7, 7, 2, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 61, 60, 60, 36, 36, 60, 36, 36, + 36, 36, 61, 61, 36, 36, 36, 36, 69, 36, 42, 42, 42, 42, 70, 43, + 36, 36, 60, 80, 42, 42, 42, 79, 7, 7, 7, 7, 7, 43, 36, 36, + 76, 66, 2, 2, 2, 2, 2, 2, 2, 96, 96, 66, 42, 66, 66, 66, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 49, 49, 49, 4, 4, 85, + 36, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, + 56, 42, 42, 42, 42, 42, 42, 84, 42, 42, 59, 42, 36, 36, 69, 42, + 42, 42, 42, 42, 56, 42, 42, 42, 42, 42, 42, 42, 42, 42, 79, 66, + 66, 66, 66, 75, 66, 66, 91, 66, 2, 2, 96, 66, 21, 63, 43, 43, + 36, 36, 36, 36, 36, 93, 86, 42, 84, 42, 42, 42, 86, 84, 86, 70, + 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 85, 42, 36, 36, 42, + 70, 85, 97, 93, 85, 85, 85, 36, 69, 42, 70, 36, 36, 36, 36, 36, + 36, 84, 86, 84, 85, 85, 86, 93, 7, 7, 7, 7, 7, 85, 86, 66, + 11, 11, 11, 47, 43, 43, 47, 43, 16, 16, 16, 16, 16, 52, 44, 16, + 36, 36, 36, 36, 60, 36, 36, 43, 36, 36, 36, 60, 60, 36, 36, 43, + 60, 36, 36, 43, 36, 36, 36, 60, 60, 36, 36, 43, 36, 36, 36, 36, + 36, 36, 36, 60, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 56, 42, + 2, 2, 2, 2, 98, 27, 27, 27, 27, 27, 27, 27, 27, 27, 99, 43, + 66, 66, 66, 66, 66, 43, 43, 43, 11, 11, 11, 43, 16, 16, 16, 43, + 100, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 76, 71, + 101, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,102,103, 43, + 36, 36, 36, 36, 36, 62, 2,104,105, 36, 36, 36, 60, 43, 43, 43, + 36, 42, 84, 43, 43, 43, 43, 61, 36, 42,106, 63, 43, 43, 43, 43, + 36, 42, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 60, 36, + 60, 42, 43, 43, 43, 43, 43, 43, 36, 36, 42, 86, 42, 42, 42, 85, + 85, 85, 85, 84, 86, 42, 42, 42, 42, 42, 2, 87, 2, 65, 69, 43, + 7, 7, 7, 7, 7, 43, 43, 43, 27, 27, 27, 27, 27, 43, 43, 43, + 2, 2, 2,107, 2, 58, 42, 83, 36, 82, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 60, 43, 43, 43, 36, 36, 69, 70, 36, 36, 36, 36, + 36, 36, 36, 36, 69, 60, 43, 43, 36, 36, 36, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 60, 42, 84, 85, 86, 84, 85, 43, 43, + 85, 84, 85, 85, 86, 42, 43, 43, 91, 43, 2, 7, 7, 7, 7, 7, + 36, 36, 36, 36, 36, 36, 36, 43, 36, 36, 60, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 43, 43, 36, 36, 36, 36, 36, 43, 43, 43, + 7, 7, 7, 7, 7, 99, 43, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 36, 36, 36, 69, 84, 86, 43, 2, 36, 36, 93, 84, 42, 42, 42, 79, + 84, 84, 86, 42, 42, 42, 84, 85, 85, 86, 42, 42, 42, 42, 79, 56, + 2, 2, 2, 87, 2, 2, 2, 43, 42, 42, 42, 42, 42, 42, 42,108, + 42, 42, 42, 42, 42, 42, 42, 43, 42, 42, 42, 42, 42, 42, 43, 43, + 42, 42, 97, 36, 36, 36, 36, 36, 36, 36, 84, 42, 42, 84, 84, 85, + 85, 84, 97, 36, 36, 36, 60, 2, 96, 66, 66, 66, 66, 49, 42, 42, + 42, 42, 66, 66, 66, 66, 21, 2, 42, 97, 36, 36, 36, 36, 36, 36, + 93, 42, 42, 85, 42, 86, 42, 36, 36, 36, 36, 84, 42, 85, 86, 86, + 42, 85, 43, 43, 43, 43, 2, 2, 36, 36, 85, 85, 85, 85, 42, 42, + 42, 42, 85, 42, 43, 92, 2, 2, 7, 7, 7, 7, 7, 43, 61, 36, + 36, 36, 36, 36, 39, 39, 39, 2, 16, 16, 16, 16, 34,109, 43, 43, + 11, 11, 11, 11, 11, 46, 47, 11, 2, 2, 2, 2, 43, 43, 43, 43, + 42, 59, 42, 42, 42, 42, 42, 42, 84, 42, 42, 42, 70, 36, 69, 36, + 36, 36, 70, 93, 42, 60, 43, 43, 16, 16, 16, 16, 16, 16, 39, 39, + 39, 39, 39, 39, 39, 44, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, + 16, 16, 16, 16, 16,110, 39, 39, 32, 32, 32, 16, 16, 16, 16, 32, + 16, 16, 16, 16, 11, 11, 11, 11, 16, 16, 16, 43, 11, 11, 11, 43, + 16, 16, 16, 16, 47, 47, 47, 47, 16, 16, 16, 16, 16, 16, 16, 43, + 16, 16, 16, 16,111,111,111,111, 16, 16,109, 16, 11, 11,112,113, + 40, 16,109, 16, 11, 11,112, 40, 16, 16, 43, 16, 11, 11,114, 40, + 16, 16, 16, 16, 11, 11,115, 40, 43, 16,109, 16, 11, 11,112,116, + 117,117,117,117,117,118, 64, 64,119,119,119, 2,120,121,120,121, + 2, 2, 2, 2,122, 64, 64,123, 2, 2, 2, 2,124,125, 2,126, + 127, 2,128,129, 2, 2, 2, 2, 2, 9,127, 2, 2, 2, 2,130, + 64, 64,131, 64, 64, 64, 64, 64,132, 43, 27, 27, 27, 8,128,133, + 27, 27, 27, 27, 27, 8,128,103, 39, 39, 39, 39, 39, 39, 80, 43, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 43, 43, 43, 43, 43, 43, 43, + 42, 42, 42, 42, 42, 42,134, 50,108, 50,108, 42, 42, 42, 42, 42, + 79, 43, 43, 43, 43, 43, 43, 43, 66,135, 66,136, 66, 34, 11, 16, + 11, 32,136, 66, 48, 11, 11, 66, 66, 66,135,135,135, 11, 11,137, + 11, 11, 35, 36,138, 66, 16, 11, 8, 8, 48, 16, 16, 26, 66,139, + 27, 27, 27, 27, 27, 27, 27, 27,104,104,104,104,104,104,104,104, + 104,140,141,104,142, 66, 43, 43, 8, 8,143, 66, 66, 8, 66, 66, + 143, 26, 66,143, 66, 66, 66,143, 66, 66, 66, 66, 66, 66, 66, 8, + 66,143,143, 66, 66, 66, 66, 66, 66, 66, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 66, 66, 66, 66, 4, 4, 66, 66, + 8, 66, 66, 66,144,145, 66, 66, 66, 66, 66, 66, 66, 66,143, 66, + 66, 66, 66, 66, 66, 26, 8, 8, 8, 8, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 8, 8, 8, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 91, 43, 43, 27, 27, 27, 27, 27, 27, 66, 66, + 66, 66, 66, 66, 66, 27, 27, 27, 66, 66, 66, 26, 66, 66, 66, 66, + 26, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 8, 8, 8, 8, + 66, 66, 66, 66, 66, 66, 66, 26, 66, 66, 66, 66, 4, 4, 4, 4, + 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 66, 66, 66, 66, 66, 66, + 8, 8,128,146, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, + 8,128,147,147,147,147,147,147,147,147,147,147,146, 8, 8, 8, + 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, + 8, 8,143, 26, 8, 8,143, 66, 66, 66, 43, 66, 66, 66, 66, 66, + 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 39, 11, + 32, 32,139, 66, 66,136, 34,148, 42, 32, 43, 43, 92, 2, 98, 2, + 16, 16, 16,149, 43, 43,149, 43, 36, 36, 36, 36, 43, 43, 43, 51, + 63, 43, 43, 43, 43, 43, 43, 56, 36, 36, 36, 60, 43, 43, 43, 43, + 36, 36, 36, 60, 36, 36, 36, 60, 2,120,120, 2,124,125,120, 2, + 2, 2, 2, 6, 2,107,120, 2,120, 4, 4, 4, 4, 2, 2, 87, + 2, 2, 2, 2, 2,119, 2, 2,107,150, 2, 2, 2, 2, 2, 2, + 66, 2,151,147,147,147,152, 43, 66, 66, 66, 66, 66, 54, 66, 66, + 66, 66, 43, 43, 43, 43, 43, 43, 66, 66, 66, 43, 43, 43, 43, 43, + 1, 2,153,154, 4, 4, 4, 4, 4, 66, 4, 4, 4, 4,155,156, + 157,104,104,104,104, 42, 42, 85,158, 39, 39, 66,104,159, 62, 66, + 36, 36, 36, 60, 56,160,161, 68, 36, 36, 36, 36, 36, 62, 39, 68, + 43, 43, 61, 36, 36, 36, 36, 36, 66, 27, 27, 66, 66, 66, 66, 66, + 66, 66, 66, 43, 43, 43, 43, 54, 66, 66, 66, 66, 66, 66, 66, 91, + 27, 27, 27, 27, 27, 66, 66, 66, 66, 66, 66, 66, 27, 27, 27, 27, + 162, 27, 27, 27, 27, 27, 27, 27, 36, 36, 82, 36, 36, 36, 36, 36, + 66, 66, 66, 91, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36,163, 2, + 7, 7, 7, 7, 7, 36, 43, 43, 32, 32, 32, 32, 32, 32, 32, 69, + 50,164, 42, 42, 42, 42, 42, 87, 32, 32, 32, 32, 32, 32, 39, 42, + 36, 36, 36,104,104,104,104,104, 42, 2, 2, 2, 43, 43, 43, 43, + 40, 40, 40,161, 39, 39, 39, 39, 40, 32, 32, 32, 32, 32, 32, 32, + 16, 32, 32, 32, 32, 32, 32, 32, 44, 16, 16, 16, 34, 34, 34, 32, + 32, 32, 32, 32, 41,165, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, - 32, 32, 11, 11, 34, 34, 32, 44, 32,150,150, 32, 32, 32, 47, 44, - 44, 40,167, 35, 40, 35, 36, 36, 36, 71, 36, 71, 36, 70, 36, 36, - 36, 94, 87, 85, 67, 67, 80, 44, 27, 27, 27, 67,168, 44, 44, 44, - 36, 36, 2, 2, 44, 44, 44, 44, 86, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 86, 86, 86, 86, 86, 86, 86, 86, 43, 44, 44, 44, 44, 2, - 43, 36, 36, 36, 2, 72, 72, 70, 36, 36, 36, 43, 43, 43, 43, 2, - 36, 36, 36, 70, 43, 43, 43, 43, 43, 86, 44, 44, 44, 44, 44, 93, - 36, 70, 86, 43, 43, 86, 43, 86,107, 2, 2, 2, 2, 2, 2, 52, - 7, 7, 7, 7, 7, 44, 44, 2, 36, 36, 70, 69, 36, 36, 36, 36, - 7, 7, 7, 7, 7, 36, 36, 61, 36, 36, 36, 36, 70, 43, 43, 85, - 87, 85, 87, 80, 44, 44, 44, 44, 36, 70, 36, 36, 36, 36, 85, 44, - 7, 7, 7, 7, 7, 44, 2, 2, 69, 36, 36, 77, 67, 94, 85, 36, - 71, 43, 71, 70, 71, 36, 36, 43, 70, 61, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 62, 83, 2, 36, 36, 36, 36, 36, 94, 43, 86, - 2, 83,169, 80, 44, 44, 44, 44, 62, 36, 36, 61, 62, 36, 36, 61, - 62, 36, 36, 61, 44, 44, 44, 44, 16, 16, 16, 16, 16,114, 40, 40, - 16, 16, 16, 16,111, 41, 44, 44, 36, 94, 87, 86, 85,107, 87, 44, - 36, 36, 44, 44, 44, 44, 44, 44, 36, 36, 36, 61, 44, 62, 36, 36, - 170,170,170,170,170,170,170,170,171,171,171,171,171,171,171,171, - 16, 16, 16,110, 44, 44, 44, 44, 44,150, 16, 16, 44, 44, 62, 71, - 36, 36, 36, 36,172, 36, 36, 36, 36, 36, 36, 61, 36, 36, 61, 61, - 36, 62, 61, 36, 36, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, - 41,117, 44, 44, 44, 44, 44, 44, 44, 62, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36,148, 44, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 44, 44, 44, 55, 36, 36, 36, 36, 36, 36,168, 67, - 2, 2, 2,152,130, 44, 44, 44, 6,173,174,148,148,148,148,148, - 148,148,130,152,130, 2,127,175, 2, 64, 2, 2,156,148,148,130, - 2,176, 8,177, 66, 2, 44, 44, 36, 36, 61, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 61, 79, 93, 2, 3, 2, 4, 5, 6, 2, - 16, 16, 16, 16, 16, 17, 18,129,130, 4, 2, 36, 36, 36, 36, 36, - 69, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, - 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 61, 44, - 20,178, 56,135, 26, 8,144, 92, 44, 44, 44, 44, 79, 65, 67, 44, - 36, 36, 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 61, 36, 62, - 2, 64, 44,179, 27, 27, 27, 27, 27, 27, 44, 55, 67, 67, 67, 67, - 105,105,143, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 27, 67, 92, - 67, 67, 67, 67, 67, 67, 92, 44, 92, 44, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 67, 50, 44,180, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 44, 44, 27, 27, 44, 44, 44, 44, 62, 36, - 155, 36, 36, 36, 36,181, 44, 44, 36, 36, 36, 43, 43, 80, 44, 44, - 36, 36, 36, 36, 36, 36, 36, 93, 36, 36, 44, 44, 36, 36, 36, 36, - 182,105,105, 44, 44, 44, 44, 44, 11, 11, 11, 11, 16, 16, 16, 16, - 11, 11, 44, 44, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 44, 44, - 36, 36, 36, 36, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, 44, 93, - 11, 11, 11, 11, 11, 47, 11, 11, 11, 47, 11,150, 16, 16, 16, 16, - 16,150, 16, 16, 16, 16, 16, 16, 16,150, 16, 16, 16,150,110, 44, - 40, 40, 40, 52, 40, 40, 40, 40, 81, 40, 40, 40, 40, 81, 44, 44, - 36, 36, 36, 44, 61, 36, 36, 36, 36, 36, 36, 62, 61, 44, 61, 62, - 36, 36, 36, 93, 27, 27, 27, 27, 36, 36, 36, 77,163, 27, 27, 27, - 44, 44, 44,179, 27, 27, 27, 27, 36, 61, 36, 44, 44,179, 27, 27, - 36, 36, 36, 27, 27, 27, 44, 93, 36, 36, 36, 36, 36, 44, 44, 93, - 36, 36, 36, 36, 44, 44, 27, 36, 44, 27, 27, 27, 27, 27, 27, 27, - 70, 43, 57, 80, 44, 44, 43, 43, 36, 36, 62, 36, 62, 36, 36, 36, - 36, 36, 36, 44, 43, 80, 44, 57, 27, 27, 27, 27,100, 44, 44, 44, - 2, 2, 2, 2, 64, 44, 44, 44, 36, 36, 36, 36, 36, 36,183, 30, - 36, 36, 36, 36, 36, 36,183, 27, 36, 36, 36, 36, 78, 36, 36, 36, - 36, 36, 70, 80, 44,179, 27, 27, 2, 2, 2, 64, 44, 44, 44, 44, - 36, 36, 36, 44, 93, 2, 2, 2, 36, 36, 36, 44, 27, 27, 27, 27, - 36, 61, 44, 44, 27, 27, 27, 27, 36, 44, 44, 44, 93, 2, 64, 44, - 44, 44, 44, 44,179, 27, 27, 27, 11, 47, 44, 44, 44, 44, 44, 44, - 16,110, 44, 44, 44, 27, 27, 27, 36, 36, 43, 43, 44, 44, 44, 44, - 7, 7, 7, 7, 7, 36, 36, 69, 11, 11, 11, 44, 57, 43, 43,159, - 16, 16, 16, 44, 44, 44, 44, 8, 27, 27, 27, 27, 27, 27, 27,100, - 36, 36, 36, 36, 36, 57,184, 44, 36, 44, 44, 44, 44, 44, 44, 44, - 44, 36, 61, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, - 27, 27, 27, 95, 44, 44, 44, 44,180, 27, 30, 2, 2, 44, 44, 44, - 36, 43, 43, 2, 2, 44, 44, 44, 36, 36,183, 27, 27, 27, 44, 44, - 87, 98, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, - 43, 43, 43, 60, 2, 2, 2, 44, 27, 27, 27, 7, 7, 7, 7, 7, - 71, 70, 71, 44, 44, 44, 44, 57, 86, 87, 43, 85, 87, 60,185, 2, - 2, 80, 44, 44, 44, 44, 79, 44, 43, 71, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 70, 43, 43, 87, 43, 43, 43, 80, 7, 7, 7, 7, 7, - 2, 2, 94, 98, 44, 44, 44, 44, 36, 70, 2, 61, 44, 44, 44, 44, - 36, 94, 86, 43, 43, 43, 43, 85, 98, 36, 63, 2, 59, 43, 60, 87, - 7, 7, 7, 7, 7, 63, 63, 2,179, 27, 27, 27, 27, 27, 27, 27, - 27, 27,100, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 86, 87, - 43, 86, 85, 43, 2, 2, 2, 71, 70, 44, 44, 44, 44, 44, 44, 44, - 36, 36, 36, 61, 61, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 62, - 36, 36, 36, 36, 63, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 70, - 86, 87, 43, 43, 43, 80, 44, 44, 43, 86, 62, 36, 36, 36, 61, 62, - 61, 36, 62, 36, 36, 57, 71, 86, 85, 86, 90, 89, 90, 89, 86, 44, - 61, 44, 44, 89, 44, 44, 62, 36, 36, 86, 44, 43, 43, 43, 80, 44, - 43, 43, 80, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 62, 44, 61, - 36, 36, 36, 62, 86, 87, 43, 43, 80, 90, 89, 89, 86, 90, 86, 85, - 71, 71, 2, 93, 64, 44, 44, 44, 57, 80, 44, 44, 44, 44, 44, 44, - 36, 36, 94, 86, 43, 43, 43, 43, 86, 43, 85, 71, 36, 63, 2, 2, - 7, 7, 7, 7, 7, 2, 93, 71, 86, 87, 43, 43, 85, 85, 86, 87, - 85, 43, 36, 72, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 94, - 86, 43, 43, 44, 86, 86, 43, 87, 60, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 36, 36, 43, 44, 86, 87, 43, 43, 43, 85, 87, 87, - 60, 2, 61, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 2, 64, 44, - 36, 36, 36, 36, 36, 70, 87, 86, 43, 43, 43, 87, 63, 44, 44, 44, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 44, 44, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 61, 57, 87, 86, 43, 43, 87, 43, 43, 44, 44, - 7, 7, 7, 7, 7, 27, 2, 97, 43, 43, 43, 43, 87, 60, 44, 44, - 27,100, 44, 44, 44, 44, 44, 62, 36, 36, 36, 61, 62, 44, 36, 36, - 36, 36, 62, 61, 36, 36, 36, 36, 86, 86, 86, 89, 90, 57, 85, 71, - 98, 87, 2, 64, 44, 44, 44, 44, 36, 36, 36, 36, 44, 36, 36, 36, - 94, 86, 43, 43, 44, 43, 86, 86, 71, 72, 90, 44, 44, 44, 44, 44, - 70, 43, 43, 43, 43, 71, 36, 36, 36, 70, 43, 43, 85, 70, 43, 60, - 2, 2, 2, 59, 44, 44, 44, 44, 70, 43, 43, 85, 87, 43, 36, 36, - 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 85, 43, 2, 72, 2, - 2, 64, 44, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 44, 44, 44, - 63, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 80, 43, 43, 43, 87, - 63, 2, 2, 44, 44, 44, 44, 44, 2, 36, 36, 36, 36, 36, 36, 36, - 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 89, 43, 43, 43, - 85, 43, 87, 80, 44, 44, 44, 44, 36, 36, 36, 61, 36, 62, 36, 36, - 70, 43, 43, 80, 44, 80, 43, 57, 43, 43, 43, 70, 44, 44, 44, 44, - 36, 36, 36, 62, 61, 36, 36, 36, 36, 36, 36, 36, 36, 86, 86, 90, - 43, 89, 87, 87, 61, 44, 44, 44, 36, 70, 85,107, 64, 44, 44, 44, - 43, 94, 36, 36, 36, 36, 36, 36, 36, 36, 86, 43, 43, 80, 44, 86, - 85, 60, 2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 7, 80, 44, 44, - 27, 27, 91, 67, 67, 67, 56, 20,168, 67, 67, 67, 67, 67, 67, 67, - 67, 44, 44, 44, 44, 44, 44, 93,105,105,105,105,105,105,105,181, - 2, 2, 64, 44, 44, 44, 44, 44, 63, 64, 44, 44, 44, 44, 44, 44, - 65, 65, 65, 65, 65, 65, 65, 65, 71, 36, 36, 70, 43, 43, 43, 43, - 43, 43, 43, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 43, - 43, 43, 43, 43, 43, 86, 87, 43, 43, 43, 60, 44, 44, 44, 44, 44, - 43, 43, 43, 60, 2, 2, 67, 67, 40, 40, 97, 44, 44, 44, 44, 44, - 7, 7, 7, 7, 7,179, 27, 27, 27, 62, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 44, 44, 62, 36, 40, 69, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 83,164, 2, 27, 27, 27, 30, 2, 64, 44, 44, - 36, 36, 36, 36, 36, 61, 44, 57, 94, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 44, 44, 44, 57, - 43, 74, 40, 40, 40, 40, 40, 40, 40, 88, 80, 44, 44, 44, 44, 44, - 86, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 62, - 40, 40, 52, 40, 40, 40, 52, 81, 36, 61, 44, 44, 44, 44, 44, 44, - 44, 61, 44, 44, 44, 44, 44, 44, 36, 61, 62, 44, 44, 44, 44, 44, - 44, 44, 36, 36, 44, 44, 44, 44, 36, 36, 36, 36, 36, 44, 50, 60, - 65, 65, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 44, - 43, 43, 43, 80, 44, 44, 44, 44, 67, 67, 67, 92, 55, 67, 67, 67, - 67, 67,186, 87, 43, 67,186, 86, 86,187, 65, 65, 65, 84, 43, 43, - 43, 76, 50, 43, 43, 43, 67, 67, 67, 67, 67, 67, 67, 43, 43, 67, - 67, 43, 76, 44, 44, 44, 44, 44, 27, 27, 44, 44, 44, 44, 44, 44, - 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 16, 16, 16,110, 16, 16, 16, 16, 16, - 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 47, 11, - 44, 47, 48, 47, 48, 11, 47, 11, 11, 11, 11, 16, 16,150,150, 16, - 16, 16,150, 16, 16, 16, 16, 16, 16, 16, 11, 48, 11, 47, 48, 11, - 11, 11, 47, 11, 11, 11, 47, 16, 16, 16, 16, 16, 11, 48, 11, 47, - 11, 11, 47, 47, 44, 11, 11, 11, 47, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, - 16, 16, 16, 44, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, - 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, - 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, - 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, - 16, 33, 16, 16, 16, 32, 44, 7, 43, 43, 43, 76, 67, 50, 43, 43, - 43, 43, 43, 43, 43, 43, 76, 67, 67, 67, 50, 67, 67, 67, 67, 67, - 67, 67, 76, 21, 2, 2, 44, 44, 44, 44, 44, 44, 44, 57, 43, 43, - 16, 16, 16, 16, 16, 39, 16, 16, 16, 16, 16, 16, 16, 16, 16,110, - 44, 44,150, 16, 16,110, 44, 44, 43, 43, 43, 80, 43, 43, 43, 43, - 43, 43, 43, 43, 80, 57, 43, 43, 43, 57, 80, 43, 43, 80, 44, 44, - 40, 40, 40, 40, 40, 40, 40, 44, 44, 44, 44, 44, 44, 44, 44, 57, - 43, 43, 43, 74, 40, 40, 40, 44, 7, 7, 7, 7, 7, 44, 44, 77, - 36, 36, 36, 36, 36, 36, 36, 80, 36, 36, 36, 36, 36, 36, 43, 43, - 7, 7, 7, 7, 7, 44, 44, 96, 36, 36, 36, 36, 36, 83, 43, 43, - 188, 7, 7, 7, 7,189, 44, 93, 36, 36, 36, 61, 36, 36, 62, 61, - 36, 36, 61,179, 27, 27, 27, 27, 16, 16, 43, 43, 43, 74, 44, 44, - 27, 27, 27, 27, 27, 27,163, 27,190, 27,100, 44, 44, 44, 44, 44, - 27, 27, 27, 27, 27, 27, 27,163, 27, 27, 27, 27, 27, 27, 27, 44, - 36, 36, 62, 36, 36, 36, 36, 36, 62, 61, 61, 62, 62, 36, 36, 36, - 36, 61, 36, 36, 62, 62, 44, 44, 44, 61, 44, 62, 62, 62, 62, 36, - 62, 61, 61, 62, 62, 62, 62, 62, 62, 61, 61, 62, 36, 61, 36, 36, - 36, 61, 36, 36, 62, 36, 61, 61, 36, 36, 36, 36, 36, 62, 36, 36, - 62, 36, 62, 36, 36, 62, 36, 36, 8, 44, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 67, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, - 27, 27, 27, 27, 27, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 44, - 44, 44, 44, 67, 67, 67, 67, 67, 67, 92, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 92, 44, 44, 44, 67, 44, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 25, 41, 41, 67, 67, 67, 67, 44, 44, 67, 67, - 67, 67, 67, 92, 44, 55, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 44, 44, 55, 67, 67, 67, 92, 44, 44, 44, 67, - 67, 67, 67, 67, 67, 67, 92, 55, 67, 92, 67, 67, 67, 67, 67, 67, - 79, 44, 44, 44, 44, 44, 44, 44,171,171,171,171,171,171,171, 44, - 171,171,171,171,171,171,171, 0, 0, 0, 29, 21, 21, 21, 23, 21, - 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, - 9, 22, 21, 18, 24, 16, 24, 5, 5, 5, 5, 22, 25, 18, 25, 0, - 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, 26, 25, 15, 15, - 24, 15, 7, 19, 15, 21, 9, 25, 9, 5, 5, 25, 5, 9, 5, 7, - 7, 7, 9, 8, 8, 5, 7, 5, 6, 6, 24, 24, 6, 24, 12, 12, - 2, 2, 6, 5, 9, 21, 9, 2, 2, 9, 25, 9, 26, 12, 11, 11, - 2, 6, 5, 21, 17, 2, 2, 26, 26, 23, 2, 12, 17, 12, 21, 12, - 12, 21, 7, 2, 2, 7, 7, 21, 21, 2, 1, 1, 21, 23, 26, 26, - 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, 12, 6, 6, 12, - 12, 26, 7, 26, 26, 7, 2, 1, 12, 2, 6, 2, 24, 7, 7, 6, - 1, 12, 12, 10, 10, 10, 10, 12, 21, 6, 2, 10, 10, 2, 15, 26, - 26, 2, 2, 21, 7, 10, 15, 7, 2, 23, 21, 26, 10, 7, 21, 15, - 15, 2, 17, 7, 29, 7, 7, 22, 18, 2, 14, 14, 14, 7, 10, 21, - 17, 21, 11, 12, 5, 2, 5, 6, 8, 8, 8, 24, 5, 24, 2, 24, - 9, 24, 24, 2, 29, 29, 29, 1, 17, 17, 20, 19, 22, 20, 27, 28, - 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, 25, 22, 18, 21, 21, 29, - 1, 2, 15, 6, 18, 6, 23, 2, 12, 11, 9, 26, 26, 9, 26, 5, - 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, 26, 22, 18, 26, 18, 25, - 18, 22, 5, 12, 2, 5, 22, 21, 21, 22, 18, 17, 26, 6, 7, 14, - 17, 22, 18, 18, 26, 14, 17, 6, 14, 6, 12, 24, 24, 6, 26, 15, - 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, 6, 10, 4, 4, 3, 3, - 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, 25, 2, 25, 24, 2, 15, - 12, 15, 14, 2, 21, 14, 7, 15, 12, 17, 21, 1, 26, 10, 10, 1, - 7, 13, 13, 2, 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 0, 10, 11, 12, 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, + 32, 32, 11, 11, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 46, 43, + 51, 39,166, 35, 39, 35, 36, 36, 36, 70, 36, 70, 36, 69, 36, 36, + 36, 93, 86, 84, 66, 66, 79, 43, 27, 27, 27, 66,167, 43, 43, 43, + 36, 36, 2, 2, 43, 43, 43, 43, 85, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 85, 85, 85, 85, 85, 85, 85, 85, 42, 43, 43, 43, 43, 2, + 42, 36, 36, 36, 2, 71, 71, 69, 36, 36, 36, 42, 42, 42, 42, 2, + 36, 36, 36, 69, 42, 42, 42, 42, 42, 85, 43, 43, 43, 43, 43, 92, + 36, 69, 85, 42, 42, 85, 42, 85,106, 2, 2, 2, 2, 2, 2, 51, + 7, 7, 7, 7, 7, 43, 43, 2, 36, 36, 69, 68, 36, 36, 36, 36, + 7, 7, 7, 7, 7, 36, 36, 60, 36, 36, 36, 36, 69, 42, 42, 84, + 86, 84, 86, 79, 43, 43, 43, 43, 36, 69, 36, 36, 36, 36, 84, 43, + 7, 7, 7, 7, 7, 43, 2, 2, 68, 36, 36, 76, 66, 93, 84, 36, + 70, 42, 70, 69, 70, 36, 36, 42, 69, 60, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 61, 82, 2, 36, 36, 36, 36, 36, 93, 42, 85, + 2, 82,168, 79, 43, 43, 43, 43, 61, 36, 36, 60, 61, 36, 36, 60, + 61, 36, 36, 60, 43, 43, 43, 43, 16, 16, 16, 16, 16,113, 39, 39, + 16, 16, 16, 16,110, 40, 43, 43, 36, 93, 86, 85, 84,106, 86, 43, + 36, 36, 43, 43, 43, 43, 43, 43, 36, 36, 36, 60, 43, 61, 36, 36, + 169,169,169,169,169,169,169,169,170,170,170,170,170,170,170,170, + 16, 16, 16,109, 43, 43, 43, 43, 43,149, 16, 16, 43, 43, 61, 70, + 36, 36, 36, 36,171, 36, 36, 36, 36, 36, 36, 60, 36, 36, 60, 60, + 36, 61, 60, 36, 36, 36, 36, 36, 36, 40, 40, 40, 40, 40, 40, 40, + 40, 22, 66, 66, 66, 66, 66, 66, 66, 77, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36,147, 66, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 66, 66, 66, 66, 36, 36, 36, 36, 36, 36,167, 66, + 2, 2, 2,151,129, 43, 43, 43, 6,172,173,147,147,147,147,147, + 147,147,129,151,129, 2,126,174, 2, 63, 2, 2,155,147,147,129, + 2,175, 8,176, 65, 2, 43, 43, 36, 36, 60, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 60, 78, 92, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18,128,129, 4, 2, 36, 36, 36, 36, 36, + 68, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 39, + 43, 36, 36, 36, 43, 36, 36, 36, 43, 36, 36, 36, 43, 36, 60, 43, + 20,177, 55,178, 26, 8,143, 91, 43, 43, 43, 43, 78, 64, 66, 43, + 36, 36, 36, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 60, 36, 61, + 2, 63, 43,179, 27, 27, 27, 27, 27, 27, 43, 54, 66, 66, 66, 66, + 104,104,142, 27, 90, 66, 66, 66, 66, 66, 66, 66, 66, 27, 66, 91, + 66, 66, 66, 66, 66, 66, 91, 43, 91, 43, 43, 43, 43, 43, 43, 43, + 66, 66, 66, 66, 66, 66, 49, 43,180, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 43, 43, 27, 27, 43, 43, 43, 43, 61, 36, + 154, 36, 36, 36, 36,181, 43, 43, 36, 36, 36, 42, 42, 79, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 92, 36, 36, 43, 43, 36, 36, 36, 36, + 182,104,104, 43, 43, 43, 43, 43, 11, 11, 11, 11, 16, 16, 16, 16, + 11, 11, 43, 43, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 43, 43, + 36, 36, 36, 36, 43, 43, 43, 43, 36, 36, 43, 43, 43, 43, 43, 92, + 11, 11, 11, 11, 11, 46, 11, 11, 11, 46, 11,149, 16, 16, 16, 16, + 16,149, 16, 16, 16, 16, 16, 16, 16,149, 16, 16, 16,149,109, 43, + 39, 39, 39, 51, 39, 39, 39, 39, 80, 39, 39, 39, 39, 80, 43, 43, + 36, 36, 36, 43, 60, 36, 36, 36, 36, 36, 36, 61, 60, 43, 60, 61, + 36, 36, 36, 92, 27, 27, 27, 27, 36, 36, 36, 76,162, 27, 27, 27, + 43, 43, 43,179, 27, 27, 27, 27, 36, 60, 36, 43, 43,179, 27, 27, + 36, 36, 36, 27, 27, 27, 43, 92, 36, 36, 36, 36, 36, 43, 43, 92, + 36, 36, 36, 36, 43, 43, 27, 36, 43, 27, 27, 27, 27, 27, 27, 27, + 69, 42, 56, 79, 43, 43, 42, 42, 36, 36, 61, 36, 61, 36, 36, 36, + 36, 36, 36, 43, 42, 79, 43, 56, 27, 27, 27, 27, 99, 43, 43, 43, + 2, 2, 2, 2, 63, 43, 43, 43, 36, 36, 36, 36, 36, 36,183, 30, + 36, 36, 36, 36, 36, 36,183, 27, 36, 36, 36, 36, 77, 36, 36, 36, + 36, 36, 69, 79, 43,179, 27, 27, 2, 2, 2, 63, 43, 43, 43, 43, + 36, 36, 36, 43, 92, 2, 2, 2, 36, 36, 36, 43, 27, 27, 27, 27, + 36, 60, 43, 43, 27, 27, 27, 27, 36, 43, 43, 43, 92, 2, 63, 43, + 43, 43, 43, 43,179, 27, 27, 27, 11, 46, 43, 43, 43, 43, 43, 43, + 16,109, 43, 43, 43, 27, 27, 27, 36, 36, 42, 42, 43, 43, 43, 43, + 7, 7, 7, 7, 7, 36, 36, 68, 11, 11, 11, 43, 56, 42, 42,158, + 16, 16, 16, 43, 43, 43, 43, 8, 27, 27, 27, 27, 27, 27, 27, 99, + 36, 36, 36, 36, 36, 56,184, 43, 36, 43, 43, 43, 43, 43, 43, 43, + 43, 36, 82, 36, 43, 43, 43, 43, 96, 66, 66, 66, 91, 43, 43, 43, + 43, 43, 43, 43, 43, 42, 42, 42, 27, 27, 27, 94, 43, 43, 43, 43, + 180, 27, 30, 2, 2, 43, 43, 43, 36, 42, 42, 2, 2, 43, 43, 43, + 36, 36,183, 27, 27, 27, 43, 43, 86, 97, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 42, 59, 2, 2, 2, 43, + 27, 27, 27, 7, 7, 7, 7, 7, 70, 69, 70, 43, 43, 43, 43, 56, + 85, 86, 42, 84, 86, 59,185, 2, 2, 79, 43, 43, 43, 43, 78, 43, + 42, 70, 36, 36, 36, 36, 36, 36, 36, 36, 36, 69, 42, 42, 86, 42, + 42, 42, 79, 7, 7, 7, 7, 7, 2, 2, 93, 97, 43, 43, 43, 43, + 36, 69, 2, 60, 43, 43, 43, 43, 36, 93, 85, 42, 42, 42, 42, 84, + 97, 36, 62, 2, 58, 42, 59, 86, 7, 7, 7, 7, 7, 62, 62, 2, + 179, 27, 27, 27, 27, 27, 27, 27, 27, 27, 99, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 85, 86, 42, 85, 84, 42, 2, 2, 2, 70, + 69, 43, 43, 43, 43, 43, 43, 43, 36, 36, 36, 60, 60, 36, 36, 61, + 36, 36, 36, 36, 36, 36, 36, 61, 36, 36, 36, 36, 62, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 69, 85, 86, 42, 42, 42, 79, 43, 43, + 42, 85, 61, 36, 36, 36, 60, 61, 60, 36, 61, 36, 36, 56, 70, 85, + 84, 85, 89, 88, 89, 88, 85, 43, 60, 43, 43, 88, 43, 43, 61, 36, + 36, 85, 43, 42, 42, 42, 79, 43, 42, 42, 79, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 61, 43, 60, 36, 36, 36, 61, 85, 86, 42, 42, + 79, 89, 88, 88, 85, 89, 85, 84, 70, 70, 2, 92, 63, 43, 43, 43, + 56, 79, 43, 43, 43, 43, 43, 43, 36, 36, 93, 85, 42, 42, 42, 42, + 85, 42, 84, 70, 36, 62, 2, 2, 7, 7, 7, 7, 7, 2, 92, 70, + 85, 86, 42, 42, 84, 84, 85, 86, 84, 42, 36, 71, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 93, 85, 42, 42, 43, 85, 85, 42, 86, + 59, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 42, 43, + 85, 86, 42, 42, 42, 84, 86, 86, 59, 2, 60, 43, 43, 43, 43, 43, + 2, 2, 2, 2, 2, 2, 63, 43, 36, 36, 36, 36, 36, 69, 86, 85, + 42, 42, 42, 86, 62, 43, 43, 43, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 60, 56, 86, + 85, 42, 42, 86, 42, 42, 43, 43, 7, 7, 7, 7, 7, 27, 2, 96, + 42, 42, 42, 42, 86, 59, 43, 43, 27, 99, 43, 43, 43, 43, 43, 61, + 36, 36, 36, 60, 61, 43, 36, 36, 36, 36, 61, 60, 36, 36, 36, 36, + 85, 85, 85, 88, 89, 56, 84, 70, 97, 86, 2, 63, 43, 43, 43, 43, + 36, 36, 36, 36, 43, 36, 36, 36, 93, 85, 42, 42, 43, 42, 85, 85, + 70, 71, 89, 43, 43, 43, 43, 43, 69, 42, 42, 42, 42, 70, 36, 36, + 36, 69, 42, 42, 84, 69, 42, 59, 2, 2, 2, 58, 43, 43, 43, 43, + 69, 42, 42, 84, 86, 42, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, + 42, 42, 42, 84, 42, 2, 71, 2, 2, 63, 43, 43, 43, 43, 43, 43, + 2, 2, 2, 2, 2, 43, 43, 43, 84, 42, 84, 84, 43, 43, 43, 43, + 62, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 79, 42, 42, 42, 86, + 62, 2, 2, 43, 43, 43, 43, 43, 2, 36, 36, 36, 36, 36, 36, 36, + 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 88, 42, 42, 42, + 84, 42, 86, 79, 43, 43, 43, 43, 36, 36, 36, 60, 36, 61, 36, 36, + 69, 42, 42, 79, 43, 79, 42, 56, 42, 42, 42, 69, 43, 43, 43, 43, + 36, 36, 36, 61, 60, 36, 36, 36, 36, 36, 36, 36, 36, 85, 85, 89, + 42, 88, 86, 86, 60, 43, 43, 43, 36, 36, 36, 36, 82, 36, 43, 43, + 36, 69, 84,106, 63, 43, 43, 43, 42, 93, 36, 36, 36, 36, 36, 36, + 36, 36, 85, 42, 42, 79, 43, 85, 84, 59, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 79, 43, 43, 27, 27, 90, 66, 66, 66, 55, 20, + 167, 66, 66, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, 43, 43, 92, + 104,104,104,104,104,104,104,181, 2, 2, 63, 43, 43, 43, 43, 43, + 62, 63, 43, 43, 43, 43, 43, 43, 64, 64, 64, 64, 64, 64, 64, 64, + 70, 36, 36, 69, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 85, 86, 42, + 42, 42, 59, 43, 43, 43, 43, 43, 42, 42, 42, 59, 2, 2, 66, 66, + 39, 39, 96, 43, 43, 43, 43, 43, 7, 7, 7, 7, 7,179, 27, 27, + 27, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 61, 36, + 39, 68, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 82,163, 2, + 27, 27, 27, 30, 2, 63, 43, 43, 11, 11, 11, 11, 46,149, 16, 16, + 16, 16, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 60, 43, 56, + 93, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 43, 43, 43, 56, 42, 73, 39, 39, 39, 39, 39, 39, + 39, 87, 79, 43, 43, 43, 43, 43, 85, 39,104,181, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 61, 36, 60, 43, 43, 43, 43, 43, 43, + 39, 39, 51, 39, 39, 39, 51, 80, 43, 60, 43, 43, 43, 43, 43, 43, + 36, 60, 61, 43, 43, 43, 43, 43, 43, 43, 36, 36, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 43, 49, 59, 64, 64, 43, 43, 43, 43, 43, 43, + 7, 7, 7, 7, 7, 66, 91, 43, 66, 66, 43, 43, 43, 66, 66, 66, + 176, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 79, 43, 43, 43, 43, + 66, 66, 66, 91, 54, 66, 66, 66, 66, 66,186, 86, 42, 66,186, 85, + 85,187, 64, 64, 64, 83, 42, 42, 42, 75, 49, 42, 42, 42, 66, 66, + 66, 66, 66, 66, 66, 42, 42, 66, 66, 42, 75, 43, 43, 43, 43, 43, + 27, 27, 43, 43, 43, 43, 43, 43, 11, 11, 11, 11, 11, 16, 16, 16, + 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, + 16, 16,109, 16, 16, 16, 16, 16, 11, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 46, 11, 43, 46, 47, 46, 47, 11, 46, 11, + 11, 11, 11, 16, 16,149,149, 16, 16, 16,149, 16, 16, 16, 16, 16, + 16, 16, 11, 47, 11, 46, 47, 11, 11, 11, 46, 11, 11, 11, 46, 16, + 16, 16, 16, 16, 11, 47, 11, 46, 11, 11, 46, 46, 43, 11, 11, 11, + 46, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, + 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 43, 11, 11, 11, 11, + 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, + 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, + 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, + 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 43, 7, + 42, 42, 42, 75, 66, 49, 42, 42, 42, 42, 42, 42, 42, 42, 75, 66, + 66, 66, 49, 66, 66, 66, 66, 66, 66, 66, 75, 21, 2, 2, 43, 43, + 43, 43, 43, 43, 43, 56, 42, 42, 16, 16, 16, 16, 16,138, 16, 16, + 16, 16, 16, 16, 16, 16, 16,109, 43, 43,149, 16, 16,109, 43, 43, + 42, 42, 42, 79, 42, 42, 42, 42, 42, 42, 42, 42, 79, 56, 42, 42, + 42, 56, 79, 42, 42, 79, 43, 43, 39, 39, 39, 39, 39, 39, 39, 43, + 43, 43, 43, 43, 43, 43, 43, 56, 42, 42, 42, 73, 39, 39, 39, 43, + 7, 7, 7, 7, 7, 43, 43, 76, 36, 36, 36, 36, 36, 36, 36, 79, + 36, 36, 36, 36, 36, 36, 42, 42, 7, 7, 7, 7, 7, 43, 43, 95, + 36, 36, 36, 36, 36, 82, 42, 42,188, 7, 7, 7, 7,189, 43, 92, + 36, 69, 36, 70, 36, 36, 36, 42, 36, 36, 69, 43, 43, 43, 43, 82, + 36, 36, 36, 60, 36, 36, 61, 60, 36, 36, 60,179, 27, 27, 27, 27, + 16, 16, 42, 42, 42, 73, 43, 43, 27, 27, 27, 27, 27, 27,162, 27, + 190, 27, 99, 43, 43, 43, 43, 43, 27, 27, 27, 27, 27, 27, 27,162, + 27, 27, 27, 27, 27, 27, 27, 43, 36, 36, 61, 36, 36, 36, 36, 36, + 61, 60, 60, 61, 61, 36, 36, 36, 36, 60, 36, 36, 61, 61, 43, 43, + 43, 60, 43, 61, 61, 61, 61, 36, 61, 60, 60, 61, 61, 61, 61, 61, + 61, 60, 60, 61, 36, 60, 36, 36, 36, 60, 36, 36, 61, 36, 60, 60, + 36, 36, 36, 36, 36, 61, 36, 36, 61, 36, 61, 36, 36, 61, 36, 36, + 8, 43, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 66, 66, 43, 43, + 54, 66, 66, 66, 66, 66, 66, 66, 27, 27, 27, 27, 27, 27, 90, 66, + 66, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, 66, 66, 66, 66, 66, + 66, 91, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 91, 43, 43, 43, + 66, 43, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 66, 25, 40, 40, + 66, 66, 66, 66, 91, 43, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, + 8, 8, 8, 8,176, 43, 43, 43, 66, 66, 66, 66, 66, 91, 43, 66, + 66, 66, 66, 91, 91, 43, 54, 66, 66, 66, 66, 66, 66, 66, 91, 54, + 66, 66, 66, 66, 66, 91, 43, 54, 66, 91, 66, 66, 66, 66, 66, 66, + 7, 7, 7, 7, 7, 91, 43, 43, 78, 43, 43, 43, 43, 43, 43, 43, + 170,170,170,170,170,170,170, 43,170,170,170,170,170,170,170, 0, + 0, 0, 29, 21, 21, 21, 23, 21, 22, 18, 21, 25, 21, 17, 13, 13, + 25, 25, 25, 21, 21, 9, 9, 9, 9, 22, 21, 18, 24, 16, 24, 5, + 5, 5, 5, 22, 25, 18, 25, 0, 23, 23, 26, 21, 24, 26, 7, 20, + 25, 1, 26, 24, 26, 25, 15, 15, 24, 15, 7, 19, 15, 21, 9, 25, + 9, 5, 5, 25, 5, 9, 5, 7, 7, 7, 9, 8, 8, 5, 6, 6, + 24, 24, 6, 24, 12, 12, 2, 2, 6, 5, 9, 21, 9, 2, 2, 9, + 25, 9, 26, 12, 11, 11, 2, 6, 5, 21, 17, 2, 2, 26, 26, 23, + 2, 12, 17, 12, 21, 12, 12, 21, 7, 2, 2, 7, 7, 21, 21, 2, + 1, 1, 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, + 12, 1, 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, 2, 1, 12, 2, + 6, 2, 24, 7, 7, 6, 1, 12, 12, 10, 10, 10, 10, 12, 21, 6, + 2, 10, 10, 2, 15, 26, 26, 2, 2, 21, 7, 10, 15, 7, 2, 23, + 21, 26, 10, 7, 21, 15, 15, 2, 17, 7, 29, 7, 7, 22, 18, 2, + 14, 14, 14, 7, 10, 21, 17, 21, 11, 12, 5, 2, 5, 6, 8, 8, + 8, 24, 5, 24, 2, 24, 9, 24, 24, 2, 29, 29, 29, 1, 17, 17, + 20, 19, 22, 20, 27, 28, 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, + 25, 22, 18, 21, 21, 29, 1, 2, 15, 6, 18, 6, 12, 11, 9, 26, + 26, 9, 26, 5, 7, 5, 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, + 26, 22, 18, 26, 18, 25, 18, 22, 5, 12, 2, 5, 22, 21, 21, 22, + 18, 17, 26, 6, 7, 14, 17, 22, 18, 18, 26, 14, 17, 6, 14, 6, + 12, 24, 24, 6, 26, 15, 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, + 6, 10, 4, 4, 3, 3, 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, + 25, 2, 25, 24, 23, 2, 2, 15, 12, 15, 14, 2, 21, 14, 7, 15, + 12, 17, 21, 1, 26, 10, 10, 1, 7, 13, 13, 2, 23, 15, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, 14, 0, + 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, + 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 21, 22, 23, + 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, + 0, 0, 0, 36, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 36, 0, 37, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 40, - 0, 0, 0, 0, 0, 0, 41, 42, 43, 0, 44, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, - 0, 0, 4, 5, 6, 7, 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, - 16, 17, 16, 18, 16, 19, 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, - 21, 19, 0, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, - 34, 0, 0, 35, 0, 0, 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, - 0, 0, 0, 0, 0, 0, 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, - 0, 0, 0, 0, 0, 55, 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, - 60, 61, 62, 63, 0, 0, 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, - 67, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 69, 0, 0, 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, - 0, 0, 0, 0, 0, 0, 0, 0, 74, 75, 0, 0, 0, 0, 76, 77, - 0, 78, 79, 0, 0, 80, 81, 0, 82, 62, 0, 83, 84, 0, 0, 85, - 86, 87, 0, 88, 0, 89, 0, 90, 0, 0, 51, 91, 51, 0, 92, 0, - 93, 0, 0, 0, 81, 0, 0, 0, 94, 95, 0, 96, 97, 98, 99, 0, - 0, 0, 0, 0, 51, 0, 0, 0, 0,100,101, 0, 0, 0, 0, 0, - 0,102, 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0, 0, 0,104, - 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,106, 0, 0,107, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,108,109, 0, 0,110, 0, 0, - 0, 0, 0, 0,111, 0,112, 0,105, 0, 0, 0, 0, 0,113,114, - 0, 0, 0, 0, 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0,117, - 0,118, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, + 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, 6, 7, 0, 8, + 9, 10, 0, 11, 12, 13, 14, 15, 16, 17, 16, 18, 16, 19, 16, 19, + 16, 19, 0, 19, 16, 20, 16, 19, 21, 19, 0, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, 35, 0, 0, 36, 0, + 37, 0, 0, 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 0, 0, 47, + 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 50, + 0, 51, 0, 52, 53, 0, 54, 0, 0, 0, 0, 0, 0, 55, 56, 57, + 0, 0, 0, 0, 58, 0, 0, 59, 60, 61, 62, 63, 0, 0, 64, 65, + 0, 0, 0, 66, 0, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 70, + 0, 71, 0, 0, 72, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, + 74, 75, 0, 0, 0, 0, 76, 77, 0, 78, 79, 0, 0, 80, 81, 0, + 82, 62, 0, 83, 84, 0, 0, 85, 86, 87, 0, 88, 0, 89, 0, 90, + 0, 0, 51, 91, 51, 0, 92, 0, 93, 0, 0, 0, 81, 0, 0, 0, + 94, 95, 0, 96, 97, 98, 99, 0, 0, 0, 0, 0, 51, 0, 0, 0, + 0,100,101, 0, 0, 0, 0, 0, 0,102, 0, 0, 0, 0, 0, 0, + 103, 0, 0, 0, 0, 0, 0,104,105, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,106, 0, 0,107, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,108,109, 0, 0,110, 0, 0, 0, 0, 0, 0,111, 0,112, 0, + 105, 0, 0, 0, 0, 0,113,114, 0, 0, 0, 0, 0, 0, 0,115, + 0, 0, 0,116, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0,118, + 0,119, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 0, 0, @@ -3371,560 +3219,384 @@ _hb_ucd_u8[17524] = 0, 43, 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 52, 0, 53, 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, - 0, 0, 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, 0, 0, 0, 61, - 52, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, 0, 0, 0, 67, - 0, 68, 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, 0, 0, 77, 78, - 0, 0, 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, 0, 81, 0, 0, - 0, 0, 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, 84, 0, 85, 0, - 52, 0, 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, 0, 0, 0, 88, - 57, 0, 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, 0, 0, 33, 0, - 0, 91, 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, 0, 0, 93, 0, - 0, 0, 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, 0, 0, 98, 0, - 0, 0, 99, 0, 0, 0,100, 0, 0, 0, 0,101,102, 93, 0, 0, - 103, 0, 0, 0, 84, 0, 0,104, 0, 0, 0,105,106, 0, 0,107, - 108, 0, 0, 0, 0, 0, 0,109, 0, 0,110, 0, 0, 0, 0,111, - 33, 0,112,113,114, 57, 0, 0,115, 35, 0, 0,116, 0, 0, 0, - 117, 0, 0, 0, 0, 0, 0,118, 0, 0,119, 0, 0, 0, 0,120, - 88, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 52,121, 0, 0, 0, - 0,122, 0, 0,123, 0, 0, 0, 0,121, 0, 0,124, 0, 0, 0, - 0, 0, 79, 0, 0, 0, 0,125, 0, 0, 0,126, 0, 0, 0,127, - 0,128, 0, 0, 0, 0,129,130,131, 0,132, 0,133, 0, 0, 0, - 134,135,136, 0, 77, 0, 0, 0, 0, 0, 35, 0, 0, 0,137, 0, - 0, 0,138, 0, 0, 0,139, 0, 0,140, 0, 0,141, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 4, - 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, 1, 1, - 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, - 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, 1, 36, - 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, 0, 0, - 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, 0, 47, - 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, 0, 19, 52, 1, - 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, 35, 1, - 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, 0, 59, - 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, 64, 0, - 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, 68, 0, - 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, 0, 0, - 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, 0, 0, - 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, 83, 0, - 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, 15, 86, - 36, 10, 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, - 0, 0, 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, - 87, 9, 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, 21, 1, 21, 92, - 93, 1, 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, 81, 99,100, 4, - 58, 0, 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, - 0, 0,101,102, 0, 0,103, 0, 0, 1, 1, 50, 0, 0, 0, 38, - 0, 63, 0, 0, 0, 0, 0, 62, 0, 0,104, 68, 61, 0, 0, 0, - 78, 0, 0, 0,105,106, 58, 38, 81, 0, 0, 0, 0, 0, 0,107, - 1, 14, 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, 0, 0, 0,108, - 0, 0,109, 61, 0,110, 0, 0, 0, 1, 0, 0, 0, 0, 49, 50, - 0, 0, 19, 58, 0, 0, 0, 51, 0,111, 14, 52,112, 41, 0, 0, - 62, 0, 0, 61, 0, 0,113, 0, 87, 0, 0, 0, 61, 62, 0, 0, - 62, 0, 89, 0, 0,113, 0, 0, 0, 0,114, 0, 0, 0, 78, 55, - 0, 38, 1, 58, 1, 58, 0, 0, 0, 0, 0, 88, 63, 89, 0, 0, - 115, 0, 0, 0, 55, 0, 0, 0, 0,115, 0, 0, 0, 0, 61, 0, - 0, 0, 0, 79, 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, - 79, 0, 0, 0, 8, 91, 0, 0, 1, 87, 0, 0,116, 0, 0, 0, - 0, 0, 0,117, 0,118,119,120,121, 0,104, 4,122, 49, 23, 0, - 0, 0, 38, 50, 38, 58, 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, - 48,105, 87, 0, 0, 0, 0, 1, 0, 0, 0,123, 0, 0, 0,112, - 4,122, 0, 0, 0, 1,124, 0, 0, 0, 0, 0,230,230,230,230, - 230,232,220,220,220,220,232,216,220,220,220,220,220,202,202,220, - 220,220,220,202,202,220,220,220, 1, 1, 1, 1, 1,220,220,220, - 220,230,230,230,230,240,230,220,220,220,230,230,230,220,220, 0, - 230,230,230,220,220,220,220,230,232,220,220,230,233,234,234,233, - 234,234,233,230, 0, 0, 0,230, 0,220,230,230,230,230,220,230, - 230,230,222,220,230,230,220,220,230,222,228,230, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, 25, 0, - 230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, 30, 31, - 32, 33, 34,230,230,220,220,230,220,230,230,220, 35, 0, 0, 0, - 0, 0,230,230,230, 0, 0,230,230, 0,220,230,230,220, 0, 0, - 0, 36, 0, 0,230,220,230,230,220,220,230,220,220,230,220,230, - 220,230,230, 0, 0,220, 0, 0,230,230, 0,230, 0,230,230,230, - 230,230, 0, 0, 0,220,220,220,230,220,220,220,230,230, 0,220, - 27, 28, 29,230, 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230, - 230, 0, 0, 0, 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, - 9, 0, 0, 0, 0, 0, 9, 0,103,103, 9, 0,107,107,107,107, - 118,118, 9, 0,122,122,122,122,220,220, 0, 0, 0,220, 0,220, - 0,216, 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130, - 130,130, 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, - 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, 0,230, 0, 0, - 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0,230, 0, 0,220, - 230,220, 0,220,230,230,230, 0, 0, 0, 9, 9, 0, 0, 7, 0, - 230, 0, 1, 1, 1, 0, 0, 0,230,234,214,220,202,230,230,230, - 230,230,232,228,228,220,218,230,233,220,230,220,230,230, 1, 1, - 1, 1, 1,230, 0, 1, 1,230,220,230, 1, 1, 0, 0,218,228, - 232,222,224,224, 0, 8, 8, 0, 0, 0, 0,220,230, 0,230,230, - 220, 0, 0,230, 0, 0, 26, 0, 0,220, 0,230,230, 1,220, 0, - 0,230,220, 0, 0, 0,220,220, 0, 0,230,220, 0, 9, 7, 0, - 0, 7, 9, 0, 0, 0, 9, 7, 6, 6, 0, 0, 0, 0, 1, 0, - 0,216,216, 1, 1, 1, 0, 0, 0,226,216,216,216,216,216, 0, - 220,220,220, 0,232,232,220,230,230,230, 7, 0, 16, 17, 17, 33, - 17, 49, 17, 17, 84, 97,135,145, 26, 17, 17, 17, 17, 17, 17, 17, + 0, 0, 57, 58, 0, 0, 0, 59, 60, 61, 62, 0, 0, 0, 0, 63, + 52, 0, 64, 65, 0, 0, 66, 0, 0, 0, 67, 68, 0, 0, 0, 69, + 0, 70, 71, 72, 73, 74, 1, 75, 0, 76, 77, 78, 0, 0, 79, 80, + 0, 0, 0, 81, 0, 0, 1, 1, 0, 0, 82, 0, 0, 83, 0, 0, + 0, 0, 79, 84, 0, 85, 0, 0, 0, 0, 0, 80, 86, 0, 87, 0, + 52, 0, 1, 80, 0, 0, 88, 0, 0, 89, 0, 0, 0, 0, 0, 90, + 57, 0, 0, 0, 0, 0, 0, 91, 92, 0, 0, 86, 0, 0, 33, 0, + 0, 93, 0, 0, 0, 0, 94, 0, 0, 0, 0, 49, 0, 0, 95, 0, + 0, 0, 0, 96, 97, 0, 0, 98, 0, 0, 99, 0, 0, 0,100, 0, + 0, 0,101, 0, 0, 0,102, 0, 0, 0, 0,103,104, 95, 0, 0, + 105, 0, 0, 0, 86, 0, 0,106, 0, 0, 0,107,108, 0, 0,109, + 110, 0, 0, 0, 0, 0, 0,111, 0, 0,112, 0, 0, 0, 0,113, + 33, 0,114,115,116, 57, 0, 0,117, 35, 0, 0,118, 0, 0, 0, + 119, 0, 0, 0, 0, 0, 0,120, 0, 0,121, 0, 0, 0, 0,122, + 90, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 52,123, 0, 0, 0, + 0,124, 0, 0,125, 0, 0, 0, 0,123, 0, 0,126, 0, 0, 0, + 0, 0, 81, 0, 0, 0, 0,127, 0, 0, 0,128, 0, 0, 0,129, + 0,130, 0, 0, 0, 0,131,132,133, 0,134, 0,135, 0, 0, 0, + 136,137,138, 0, 79, 0, 0, 0, 0, 0, 35, 0, 0, 0,139, 0, + 0, 0,140, 0, 0, 0,141, 0, 0, 0,142,143, 0,144, 0, 0, + 145, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, + 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, + 18, 1, 1, 1, 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, + 25, 26, 27, 28, 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, + 34, 35, 1, 36, 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, + 42, 0, 0, 0, 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, + 21, 0, 0, 47, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, + 0, 19, 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, + 54, 21, 35, 1, 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, + 0, 0, 0, 59, 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, + 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, + 0, 0, 68, 0, 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, + 0, 77, 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, + 0, 80, 0, 0, 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, + 0, 0, 83, 0, 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, + 1, 52, 15, 86, 36, 10, 21, 1, 1, 1, 1, 41, 1, 21, 87, 0, + 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 88, 0, + 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, 90, 9, 12, 4, + 91, 8, 92, 47, 0, 58, 50, 0, 21, 1, 21, 93, 94, 1, 1, 1, + 1, 95, 96, 97, 98, 1, 99, 58, 81,100,101, 4, 58, 0, 0, 0, + 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, 0, 0,102,103, + 0, 0,104, 0, 0, 1, 1, 50, 0, 0, 0, 38, 0, 63, 0, 0, + 0, 0, 0, 62, 0, 0,105, 68, 61, 0, 0, 0, 78, 0, 0, 0, + 106,107, 58, 38, 81, 0, 0, 0, 0, 0, 0,108, 1, 14, 4, 12, + 84, 0, 0, 0, 0, 38, 90, 0, 0, 0, 0,109, 0, 0,110, 61, + 0,111, 0, 0, 0, 1, 0, 0, 0, 0, 49, 50, 0, 0, 19, 58, + 0, 0,112, 51, 0,112, 14, 52,113, 41, 0, 0, 62, 0, 0, 61, + 0, 0,114, 0, 90, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, + 0,114, 0, 0, 0, 0,115, 0, 0, 0, 78, 55, 0, 38, 1, 58, + 1, 58, 0, 0, 0, 0, 0, 88, 63, 89, 0, 0,116, 0, 0, 0, + 55, 0, 0, 0, 0,116, 0, 0, 0, 0, 61, 0, 0, 0, 0, 79, + 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, 0, 0, + 8, 92, 0, 0, 1, 90, 0, 0,117, 0, 0, 0, 0, 0, 0,118, + 0,119,120,121,122, 0,105, 4,123, 49, 23, 0, 0, 0, 38, 50, + 38, 58, 0, 0, 1, 90, 1, 1, 1, 1, 39, 1, 48,106, 90, 0, + 0, 0, 0, 1, 0, 0, 0,124, 0, 0, 0,113, 19, 59, 0, 38, + 0, 81, 0, 0, 4,123, 0, 0, 0, 1,125, 0, 0, 0, 0, 0, + 230,230,230,230,230,232,220,220,220,220,232,216,220,220,220,220, + 220,202,202,220,220,220,220,202,202,220,220,220, 1, 1, 1, 1, + 1,220,220,220,220,230,230,230,230,240,230,220,220,220,230,230, + 230,220,220, 0,230,230,230,220,220,220,220,230,232,220,220,230, + 233,234,234,233,234,234,233,230, 0, 0, 0,230, 0,220,230,230, + 230,230,220,230,230,230,222,220,230,230,220,220,230,222,228,230, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, + 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, + 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230,220,230,230,220, + 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230,230, 0,220,230, + 230,220, 0, 0, 0, 36, 0, 0,230,220,230,230,220,220,230,220, + 220,230,220,230,220,230,230, 0, 0,220, 0, 0,230,230, 0,230, + 0,230,230,230,230,230, 0, 0, 0,220,220,220,230,220,220,220, + 230,230, 0,220, 27, 28, 29,230, 7, 0, 0, 0, 0, 9, 0, 0, + 0,230,220,230,230, 0, 0, 0, 0, 0,230, 0, 0, 84, 91, 0, + 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 9, 0,103,103, 9, 0, + 107,107,107,107,118,118, 9, 0,122,122,122,122,220,220, 0, 0, + 0,220, 0,220, 0,216, 0, 0, 0,129,130, 0,132, 0, 0, 0, + 0, 0,130,130,130,130, 0, 0,130, 0,230,230, 9, 0,230,230, + 0, 0,220, 0, 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, + 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0, + 230, 0, 0,220,230,220, 0,220,230,230,230,234, 0, 0, 9, 9, + 0, 0, 7, 0,230,230,230, 0,230, 0, 1, 1, 1, 0, 0, 0, + 230,234,214,220,202,230,230,230,230,230,232,228,228,220,218,230, + 233,220,230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230, + 220,230, 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, + 0, 0, 0,220,230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, + 0,220, 0,230,230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, + 0, 0,230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, + 6, 6, 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, + 0,226,216,216,216,216,216, 0,220,220,220, 0,232,232,220,230, + 230,230, 7, 0, 16, 17, 17, 33, 17, 49, 17, 17, 84, 97,135,145, + 26, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,177, 0, 1, 2, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 3, 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, 6, 7, 8, 3, - 3, 3, 3, 3, 9, 10, 11, 12, 13, 3, 3, 3, 3, 3, 3, 3, - 3, 14, 3, 15, 3, 3, 3, 3, 3, 3, 16, 17, 18, 19, 20, 21, - 3, 3, 3, 22, 23, 24, 3, 3, 3, 3, 3, 3, 25, 3, 3, 3, - 3, 3, 3, 3, 3, 26, 3, 3, 27, 28, 0, 1, 0, 0, 0, 0, - 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, - 0, 4, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 9, 0, 9, 0, 0, - 0, 0, 0, 0, 0, 10, 11, 12, 13, 0, 0, 14, 15, 16, 6, 0, - 17, 18, 19, 19, 19, 20, 21, 22, 23, 24, 19, 25, 0, 26, 27, 19, - 19, 28, 29, 30, 0, 31, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, - 0, 19, 28, 0, 32, 33, 9, 34, 35, 19, 0, 0, 36, 37, 38, 39, - 40, 19, 0, 41, 42, 43, 44, 31, 0, 1, 45, 42, 0, 0, 0, 0, - 0, 32, 14, 14, 0, 0, 0, 0, 14, 0, 0, 46, 47, 47, 47, 47, - 48, 49, 47, 47, 47, 47, 50, 51, 52, 53, 43, 21, 0, 0, 0, 0, - 0, 0, 0, 54, 6, 55, 0, 14, 19, 1, 0, 0, 0, 0, 56, 57, - 0, 0, 0, 0, 0, 19, 58, 31, 0, 0, 0, 0, 0, 0, 0, 59, - 14, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 60, - 61, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 3, - 0, 4, 5, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 1, 1, 0, - 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, 8, 9, 10, 11, 12, 0, - 0, 0, 13, 0, 0, 0, 0, 14, 15, 16, 17, 0, 0, 0, 1, 0, - 0, 18, 19, 0, 0, 0, 20, 0, 0, 0, 1, 1, 1, 1, 0, 1, - 1, 1, 1, 1, 1, 1, 0, 8, 21, 9, 0, 0, 22, 0, 0, 0, - 0, 1, 0, 23, 24, 25, 0, 0, 26, 0, 0, 0, 8, 21, 27, 0, - 1, 0, 0, 1, 1, 1, 1, 0, 1, 28, 29, 30, 0, 31, 32, 20, - 1, 1, 0, 0, 0, 8, 21, 9, 1, 4, 5, 0, 0, 0, 33, 9, - 0, 1, 1, 1, 0, 8, 21, 21, 21, 21, 34, 1, 35, 21, 21, 21, - 9, 36, 0, 0, 37, 38, 1, 0, 39, 0, 0, 0, 1, 0, 1, 0, - 0, 0, 0, 8, 21, 9, 1, 0, 0, 0, 40, 0, 8, 21, 21, 21, - 21, 21, 21, 21, 21, 9, 0, 1, 1, 1, 1, 8, 21, 21, 21, 9, - 0, 0, 0, 41, 0, 42, 43, 0, 0, 0, 1, 44, 0, 0, 0, 45, - 8, 9, 1, 0, 0, 0, 8, 21, 21, 21, 9, 0, 1, 0, 1, 1, - 8, 21, 21, 9, 0, 4, 5, 8, 9, 1, 0, 0, 0, 1, 2, 3, - 4, 5, 5, 5, 5, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 9, 16, 17, 18, 9, 19, 20, 21, 22, 23, 24, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 25, 26, 27, 5, 28, 29, 5, 30, 31, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 32, 0, 0, 1, - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 20, 21, 22, 23, 24, 25, 26, 27, 28, 1, 29, 30, 31, - 32, 32, 33, 32, 32, 32, 34, 32, 32, 35, 36, 37, 38, 39, 40, 41, - 42, 43, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 46, 46, - 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, - 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, - 56, 56, 56, 56, 56, 56, 56, 44, 57, 58, 59, 60, 61, 62, 63, 64, - 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 95, - 95, 96, 97, 98, 56, 56, 56, 56, 56, 56, 56, 56, 56, 99,100,100, - 100,100,101,100,100,100,100,100,100,100,100,100,100,100,100,100, - 100,102,103,103,104, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,105, - 56, 56, 56, 56, 56, 56,106,106,107,108, 56,109,110,111,112,112, - 112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, - 112,112,112,112,112,113,112,112,112,114,115,116, 56, 56, 56, 56, - 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,117,118,119, - 120, 56, 56, 56, 56, 56, 56, 56, 56, 56,121, 56, 56, 56, 56, 56, - 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,122, 32,123,124,125,126, - 127,128,129,130,131,132,133,133,134, 56, 56, 56, 56,135,136,137, - 138, 56,139,140, 56,141,142,143, 56, 56,144,145,146, 56,147,148, - 149, 32, 32, 32,150,151,152, 32,153,154, 56, 56, 56, 56, 44, 44, - 44, 44, 44, 44,155, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44,156,157, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,158, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44,159, 44, 44,160, 56, 56, 56, - 56, 56, 56, 56, 56, 56, 44, 44,161, 56, 56, 56, 56, 56, 44, 44, - 44,162, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44,163, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,164,165, - 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, 0, 0, - 19, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 0, 19, 0, - 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, - 26, 26, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, - 9, 9, 0, 9, 9, 9, 2, 2, 9, 9, 9, 9, 0, 9, 2, 2, - 2, 2, 9, 0, 9, 0, 9, 9, 9, 2, 9, 2, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 6, 2, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 2, 4, 4, 4, 2, 2, 4, 4, 4, 2, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 2, - 2, 2, 2, 2, 2, 2, 14, 14, 14, 2, 2, 2, 2, 14, 14, 14, - 14, 14, 14, 2, 2, 2, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, - 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 3, - 3, 3, 3, 3, 3, 3, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 2, 37, 37, 37, 37, 2, 2, 37, 37, 37, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 2, 2, 2, 2, 2, 2, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 2, 2, 64, 64, 64, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 2, 2, 90, 90, - 90, 90, 90, 90, 90, 2, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 2, 2, 95, 2, 37, 37, 37, 2, 2, 2, 2, 2, 3, 3, - 3, 3, 3, 3, 3, 2, 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, - 0, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, - 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 5, 5, - 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 2, - 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, - 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, - 2, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 2, 2, 2, - 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 5, 5, 2, 5, 5, 5, - 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 11, - 11, 11, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, - 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, - 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, - 2, 2, 11, 2, 11, 11, 11, 2, 2, 11, 11, 11, 2, 2, 2, 11, - 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, 2, 11, 2, 2, 2, - 2, 2, 2, 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 10, - 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, - 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, - 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, 10, - 2, 2, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 2, 2, 10, 2, - 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, - 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 2, 21, - 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, - 2, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, - 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 2, 21, 21, 21, 21, 21, - 2, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 2, 2, 2, 2, - 2, 2, 2, 21, 21, 21, 2, 2, 2, 2, 21, 21, 2, 21, 21, 21, - 21, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, - 22, 2, 2, 2, 22, 22, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, - 22, 2, 22, 2, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 2, 2, 2, 2, 22, 22, 22, 2, - 2, 2, 2, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, - 22, 2, 2, 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 2, 23, 23, 23, 2, 23, 23, 23, 23, 23, 23, 23, 23, - 2, 2, 23, 23, 23, 23, 23, 2, 23, 23, 23, 23, 2, 2, 2, 2, - 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 23, 2, 2, 23, 23, - 23, 23, 2, 2, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 2, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 16, - 2, 2, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 2, 2, 2, 2, - 2, 2, 2, 16, 16, 2, 16, 16, 16, 16, 2, 2, 16, 16, 2, 16, - 16, 16, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 2, 20, 20, 20, 2, 20, 20, 20, 20, 20, 20, 2, 2, - 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 2, 2, 20, 20, 2, 36, - 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 2, 2, 2, 36, 36, 36, 36, 36, 36, 36, 36, - 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, 2, 2, - 36, 2, 2, 2, 2, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, 2, - 2, 2, 2, 2, 36, 36, 2, 2, 36, 36, 36, 2, 2, 2, 2, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 2, 2, 2, 2, 0, 24, 24, 24, 24, 2, 2, 2, 2, 2, 18, - 18, 2, 18, 2, 18, 18, 18, 18, 18, 2, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, 18, - 18, 18, 18, 18, 2, 2, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, - 18, 18, 18, 18, 18, 2, 18, 18, 2, 2, 18, 18, 18, 18, 25, 25, - 25, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 25, 25, - 25, 25, 25, 2, 2, 2, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, - 25, 25, 25, 0, 0, 0, 0, 25, 25, 2, 2, 2, 2, 2, 33, 33, - 33, 33, 33, 33, 33, 33, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 2, 8, 2, 2, 2, 2, 2, 8, 2, 2, 8, 8, - 8, 0, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, - 30, 30, 30, 30, 30, 2, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, - 30, 30, 30, 2, 2, 2, 30, 30, 2, 2, 2, 2, 2, 2, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 2, 2, 28, 28, - 28, 28, 28, 28, 28, 28, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 2, 2, 2, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 0, 0, 0, 35, 35, 35, 2, 2, 2, 2, 2, 2, 2, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 0, 0, 2, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 2, 2, 2, 2, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 2, 46, 46, 46, 2, 46, 46, 2, 2, 2, 2, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 2, 2, 31, 31, - 2, 2, 2, 2, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 2, 2, 2, 2, 2, 2, 32, 2, - 2, 2, 2, 2, 2, 2, 32, 32, 32, 2, 2, 2, 2, 2, 28, 28, - 28, 28, 28, 28, 2, 2, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 2, 48, 48, 48, 48, 2, 2, 2, 2, 48, 2, - 2, 2, 48, 48, 48, 48, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 2, 2, 52, 52, 52, 52, 52, 2, 2, 2, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 2, 2, 2, 2, 58, 58, - 2, 2, 2, 2, 2, 2, 58, 58, 58, 2, 2, 2, 58, 58, 54, 54, - 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 2, 2, 54, 54, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 2, 91, 91, - 91, 91, 91, 2, 2, 91, 91, 91, 2, 2, 2, 2, 2, 2, 91, 91, - 91, 91, 91, 91, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 2, 62, 62, 76, 76, - 76, 76, 76, 76, 76, 76, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 2, 2, 2, 2, 2, 2, 2, 2, 93, 93, 93, 93, 70, 70, - 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 70, 70, 70, 70, - 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 73, 73, 73, 73, 6, 6, - 6, 2, 2, 2, 2, 2, 8, 8, 8, 2, 2, 8, 8, 8, 1, 1, - 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, - 0, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, - 9, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, - 19, 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 19, 6, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, - 9, 9, 9, 9, 2, 2, 2, 9, 2, 9, 2, 9, 2, 9, 9, 9, - 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, - 9, 9, 2, 9, 9, 9, 2, 2, 9, 9, 9, 2, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 2, 0, 0, 0, 19, 2, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 2, 19, 19, - 19, 19, 19, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, - 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 19, 0, - 0, 0, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, 27, 0, 0, - 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 56, 56, - 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 2, 2, 2, 2, 2, 55, - 55, 55, 55, 55, 55, 55, 61, 61, 61, 61, 61, 61, 61, 61, 2, 2, - 2, 2, 2, 2, 2, 61, 61, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 13, 13, - 13, 13, 13, 13, 2, 2, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 13, 13, - 13, 13, 0, 0, 0, 0, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, + 17, 17, 17,177, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 5, 3, + 3, 3, 3, 3, 6, 7, 8, 3, 3, 3, 3, 3, 9, 10, 11, 12, + 13, 3, 3, 3, 3, 3, 3, 3, 3, 14, 3, 15, 3, 3, 3, 3, + 3, 3, 16, 17, 18, 19, 20, 21, 3, 3, 3, 22, 23, 24, 3, 3, + 3, 3, 3, 3, 25, 3, 3, 3, 3, 3, 3, 3, 3, 26, 3, 3, + 27, 28, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, + 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, 0, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, + 0, 0, 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, + 13, 0, 0, 14, 15, 16, 6, 0, 17, 18, 19, 19, 19, 20, 21, 22, + 23, 24, 19, 25, 0, 26, 27, 19, 19, 28, 29, 30, 0, 31, 0, 0, + 0, 8, 0, 0, 0, 0, 0, 0, 0, 19, 28, 0, 32, 33, 9, 34, + 35, 19, 0, 0, 36, 37, 38, 39, 40, 19, 0, 41, 42, 43, 44, 31, + 0, 1, 45, 42, 0, 0, 0, 0, 0, 32, 14, 14, 0, 0, 0, 0, + 14, 0, 0, 46, 47, 47, 47, 47, 48, 49, 47, 47, 47, 47, 50, 51, + 52, 53, 43, 21, 0, 0, 0, 0, 0, 0, 0, 54, 6, 55, 0, 14, + 19, 1, 0, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 19, 58, 31, + 0, 0, 0, 0, 0, 0, 0, 59, 14, 0, 0, 0, 0, 1, 0, 2, + 0, 0, 0, 3, 0, 0, 0, 60, 61, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 2, 3, 0, 4, 5, 0, 0, 6, 0, 0, + 0, 7, 0, 0, 0, 1, 1, 0, 0, 8, 9, 0, 8, 9, 0, 0, + 0, 0, 8, 9, 10, 11, 12, 0, 0, 0, 13, 0, 0, 0, 0, 14, + 15, 16, 17, 0, 0, 0, 1, 0, 0, 18, 19, 0, 0, 0, 20, 0, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 8, + 21, 9, 0, 0, 22, 0, 0, 0, 0, 1, 0, 23, 24, 25, 0, 0, + 26, 0, 0, 0, 8, 21, 27, 0, 1, 0, 0, 1, 1, 1, 1, 0, + 1, 28, 29, 30, 0, 31, 32, 20, 1, 1, 0, 0, 0, 8, 21, 9, + 1, 4, 5, 0, 0, 0, 33, 9, 0, 1, 1, 1, 0, 8, 21, 21, + 21, 21, 34, 1, 35, 21, 21, 21, 9, 36, 0, 0, 37, 38, 1, 0, + 39, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 8, 21, 9, 1, 0, + 0, 0, 40, 0, 8, 21, 21, 21, 21, 21, 21, 21, 21, 9, 0, 1, + 1, 1, 1, 8, 21, 21, 21, 9, 0, 0, 0, 41, 0, 42, 43, 0, + 0, 0, 1, 44, 0, 0, 0, 45, 8, 9, 1, 0, 0, 0, 8, 21, + 21, 21, 9, 0, 1, 0, 1, 1, 8, 21, 21, 9, 0, 4, 5, 8, + 9, 1, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 11, 11, 11, 11, 12, 13, + 13, 13, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 13, 13, 13, + 24, 25, 26, 26, 26, 27, 13, 13, 13, 28, 29, 30, 13, 31, 32, 33, + 34, 35, 36, 37, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 38, 7, 7, 39, 7, 40, 7, 7, + 7, 41, 13, 42, 7, 7, 43, 7, 7, 7, 44, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 45, 0, 0, 1, 2, 2, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 37, + 37, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 2, 2, 53, 54, 55, 56, 57, 58, 59, 59, 59, 59, 60, 59, + 59, 59, 59, 59, 59, 59, 61, 61, 59, 59, 59, 59, 62, 59, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 59, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 78, 69, 69, 69, 69, 79, 79, 79, 79, 79, 79, 79, 79, 79, 80, + 81, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 94, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 69, 69, 96, 97, 98, 99, 99, 99, + 100,101,102,103,104,105,106,107,108,109, 95,110,111,112,113,114, + 115,116,117,117,118,119,120,121,122,123,124,125,126,127,128,129, + 130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145, + 95,146,147,148,149, 95,150,151,152,153,154,155,156,157,158,159, + 160,161, 95,162,163,164,165,165,165,165,165,165,165,166,167,165, + 168, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95,169,170,170,170,170,170,170,170,170,171,170, + 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, + 170,170,170,170,170,170,170,170,170,170,170,170,170,172,173,173, + 173,173,174, 95, 95, 95, 95, 95,175, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95,176,176,176,176,177,178,179,180, 95, 95, + 181, 95,182,183,184,185,186,186,186,186,186,186,186,186,186,186, + 186,186,186,186,186,186,186,186,186,186,186,186,187,187,187,188, + 189,190, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95,191,192,193,194,195,195,196, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,197,198, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 59,199, + 59, 59, 59,200,201,202, 59,203,204,205,206,207,208, 95,209,210, + 211, 59, 59,212, 59,213,214,214,214,214,214,215, 95, 95, 95, 95, + 95, 95, 95, 95,216, 95,217,218,219, 95, 95,220, 95, 95, 95,221, + 95,222, 95,223, 95,224,225,226,227, 95, 95, 95, 95, 95,228,229, + 230, 95,231,232, 95, 95,233,234, 59,235,236, 95, 59, 59, 59, 59, + 59, 59, 59,237, 59,238,239,240, 59, 59,241,242, 59,243, 95, 95, + 95, 95, 95, 95, 95, 95, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69,244, 69, 69,245, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69,246, 69, 69, 69, 69, 69, 69, 69, 69, 69,247, 69, 69, + 69, 69,248, 95, 95, 95, 69, 69, 69, 69,249, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 69, 69, 69, 69, 69, 69,250, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69,251, 95, + 95, 95, 95, 95, 95, 95,252, 95,253,254, 0, 1, 2, 2, 0, 1, + 2, 2, 2, 3, 4, 5, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, 19, 0, 0, 0, 0, 0, + 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, 2, 2, + 9, 9, 9, 9, 0, 9, 2, 2, 2, 2, 9, 0, 9, 0, 9, 9, + 9, 2, 9, 2, 9, 9, 9, 9, 2, 9, 9, 9, 55, 55, 55, 55, + 55, 55, 6, 6, 6, 6, 6, 1, 1, 6, 2, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 2, 2, 2, 2, 14, 14, 2, 2, 2, 3, 3, 3, 3, 3, 0, + 3, 3, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, 1, 1, + 3, 3, 1, 3, 3, 3, 37, 37, 37, 37, 37, 37, 2, 37, 37, 37, + 37, 2, 2, 37, 37, 37, 38, 38, 38, 38, 38, 38, 2, 2, 64, 64, + 64, 64, 64, 64, 64, 2, 2, 64, 64, 64, 90, 90, 90, 90, 90, 90, + 2, 2, 90, 90, 90, 2, 95, 95, 95, 95, 2, 2, 95, 2, 3, 3, + 2, 2, 2, 2, 2, 3, 3, 3, 0, 3, 7, 7, 7, 7, 7, 1, + 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, 2, 5, + 5, 5, 5, 2, 2, 5, 5, 2, 5, 5, 5, 2, 5, 2, 2, 2, + 5, 5, 5, 5, 2, 2, 5, 5, 5, 2, 2, 2, 2, 5, 5, 5, + 2, 5, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, + 2, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, + 2, 2, 2, 11, 2, 2, 11, 2, 11, 2, 2, 2, 11, 11, 2, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, + 2, 2, 10, 2, 2, 2, 2, 2, 10, 10, 2, 21, 21, 21, 21, 21, + 21, 21, 21, 2, 2, 21, 21, 2, 21, 21, 21, 21, 2, 2, 21, 21, + 2, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, + 22, 2, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, + 2, 22, 22, 2, 2, 2, 22, 22, 22, 22, 23, 23, 23, 23, 23, 2, + 23, 23, 23, 23, 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 2, + 23, 23, 2, 2, 2, 23, 16, 16, 16, 16, 16, 2, 16, 16, 2, 16, + 16, 16, 16, 16, 2, 2, 2, 16, 16, 2, 16, 16, 16, 2, 2, 2, + 16, 16, 20, 20, 20, 20, 20, 2, 20, 20, 2, 2, 20, 20, 2, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, 36, 36, 36, 36, + 2, 36, 2, 36, 2, 2, 2, 2, 36, 2, 2, 2, 2, 36, 36, 2, + 36, 2, 36, 2, 2, 2, 2, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 2, 2, 2, 2, 0, 2, 18, 18, 2, 18, 2, 18, 18, 18, 18, + 18, 2, 18, 18, 18, 18, 2, 18, 2, 18, 18, 18, 2, 2, 18, 2, + 18, 2, 25, 25, 25, 25, 2, 25, 25, 25, 25, 2, 2, 2, 25, 2, + 25, 25, 25, 0, 0, 0, 0, 25, 25, 2, 33, 33, 33, 33, 8, 8, + 8, 8, 8, 8, 2, 8, 2, 8, 2, 2, 8, 8, 8, 0, 12, 12, + 12, 12, 30, 30, 30, 30, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, + 30, 2, 2, 30, 30, 30, 30, 2, 2, 2, 29, 29, 29, 29, 29, 29, + 2, 2, 28, 28, 28, 28, 34, 34, 34, 34, 34, 2, 2, 2, 35, 35, + 35, 35, 35, 35, 35, 0, 0, 0, 35, 35, 35, 2, 2, 2, 45, 45, + 45, 45, 45, 45, 2, 2, 2, 2, 2, 45, 44, 44, 44, 44, 44, 0, + 0, 2, 43, 43, 43, 43, 46, 46, 46, 46, 46, 2, 46, 46, 31, 31, + 31, 31, 31, 31, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, + 32, 32, 32, 32, 2, 2, 32, 2, 2, 2, 32, 32, 32, 2, 28, 28, + 2, 2, 48, 48, 48, 48, 48, 48, 48, 2, 48, 2, 2, 2, 52, 52, + 52, 52, 52, 52, 2, 2, 52, 2, 2, 2, 58, 58, 58, 58, 58, 58, + 2, 2, 58, 58, 58, 2, 2, 2, 58, 58, 54, 54, 54, 54, 2, 2, + 54, 54, 91, 91, 91, 91, 91, 91, 91, 2, 91, 2, 2, 91, 91, 91, + 2, 2, 1, 1, 2, 2, 62, 62, 62, 62, 62, 2, 62, 62, 76, 76, + 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, + 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 6, 6, 6, 2, 8, 8, + 8, 2, 2, 8, 8, 8, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, + 0, 0, 0, 1, 0, 0, 1, 1, 0, 2, 19, 19, 9, 9, 9, 9, + 9, 6, 19, 9, 9, 9, 9, 9, 19, 19, 9, 9, 9, 19, 6, 19, + 19, 19, 19, 19, 19, 9, 9, 9, 2, 2, 2, 9, 2, 9, 2, 9, + 9, 9, 1, 1, 0, 0, 0, 2, 0, 0, 0, 19, 2, 2, 0, 0, + 0, 19, 0, 0, 0, 2, 19, 2, 2, 2, 0, 0, 2, 2, 1, 2, + 2, 2, 0, 0, 9, 0, 0, 0, 19, 19, 27, 27, 27, 27, 2, 2, + 0, 0, 56, 56, 56, 56, 2, 55, 55, 55, 61, 61, 61, 61, 2, 2, + 2, 61, 61, 2, 2, 2, 13, 13, 13, 13, 13, 13, 2, 13, 13, 13, + 2, 2, 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, 1, 1, 1, 1, + 12, 12, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, 1, 0, 0, 15, 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 17, 17, 17, 2, 2, - 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, - 2, 2, 2, 2, 2, 0, 12, 12, 12, 12, 12, 12, 12, 0, 17, 17, - 17, 17, 17, 17, 17, 0, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 2, 2, 2, 39, 39, 39, 39, 39, 39, 39, 2, 86, 86, - 86, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 2, 2, 2, 2, 79, 79, 79, 79, 79, 79, 79, 79, 0, 0, - 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 2, 2, 19, 19, 2, 19, 2, 19, 19, 19, 2, 2, - 19, 19, 19, 19, 19, 19, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 2, 2, 2, 65, 65, 65, 65, 65, 65, 65, 65, 75, 75, - 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 2, 2, 2, 2, - 2, 2, 2, 2, 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 0, 69, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 74, 12, 12, 12, 12, 12, 2, 2, 2, 84, 84, - 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 2, 0, 84, 84, - 2, 2, 2, 2, 84, 84, 33, 33, 33, 33, 33, 33, 33, 2, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 2, 68, 68, - 68, 68, 68, 68, 2, 2, 68, 68, 2, 2, 68, 68, 68, 68, 92, 92, - 92, 92, 92, 92, 92, 92, 92, 92, 92, 2, 2, 2, 2, 2, 2, 2, - 2, 92, 92, 92, 92, 92, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 2, 2, 30, 30, 30, 30, 30, 30, 2, 19, 19, - 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 19, 19, 19, 19, - 0, 0, 2, 2, 2, 2, 87, 87, 87, 87, 87, 87, 2, 2, 87, 87, - 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, - 2, 12, 12, 12, 12, 12, 13, 13, 2, 2, 2, 2, 2, 2, 19, 19, - 19, 19, 19, 19, 19, 2, 2, 2, 2, 4, 4, 4, 4, 4, 2, 2, - 2, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 14, 14, - 14, 14, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, 14, 3, 3, - 3, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 0, 0, 2, 2, 3, 3, 3, 3, 3, 3, 2, 2, - 2, 2, 2, 2, 2, 3, 1, 1, 1, 1, 1, 1, 6, 6, 0, 0, - 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, - 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 0, 2, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, - 17, 17, 17, 17, 0, 0, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2, - 12, 12, 12, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 2, 49, 49, 49, 2, 49, 49, 2, 49, 49, 49, - 49, 49, 49, 49, 2, 2, 49, 49, 49, 2, 2, 2, 2, 2, 0, 0, - 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, - 0, 0, 0, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 1, 2, 2, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 67, 67, 67, 67, 2, - 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, - 41, 2, 2, 2, 2, 2,118,118,118,118,118,118,118,118,118,118, - 118, 2, 2, 2, 2, 2, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, - 59, 59, 2, 2, 2, 2, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, - 40, 40, 40, 40, 40, 40, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 2, 2, 50, 50, - 2, 2, 2, 2, 2, 2,135,135,135,135,135,135,135,135,135,135, - 135,135, 2, 2, 2, 2,106,106,106,106,106,106,106,106,104,104, - 104,104,104,104,104,104,104,104,104,104, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2,104,161,161,161,161,161,161,161,161,161,161, - 161, 2,161,161,161,161,161,161,161, 2,161,161, 2,161,161,161, - 2,161,161,161,161,161,161,161, 2,161,161, 2, 2, 2,170,170, - 170,170,170,170,170,170,170,170,170,170, 2, 2, 2, 2,110,110, - 110,110,110,110,110,110,110,110,110,110,110,110,110, 2,110,110, - 110,110,110,110, 2, 2, 19, 19, 19, 19, 19, 19, 2, 19, 19, 2, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 47, 47, - 47, 47, 47, 47, 2, 2, 47, 2, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 2, 47, 47, 2, - 2, 2, 47, 2, 2, 47, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 2, 81,120,120,120,120,120,120,120,120,116,116, - 116,116,116,116,116,116,116,116,116,116,116,116,116, 2, 2, 2, - 2, 2, 2, 2, 2,116,128,128,128,128,128,128,128,128,128,128, - 128, 2,128,128, 2, 2, 2, 2, 2,128,128,128,128,128, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 2, 2, 2, 66, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 2, 2, 2, 2, 2, 72, 98, 98, - 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 2, 2, - 2, 2, 97, 97, 97, 97, 2, 2, 97, 97, 97, 97, 97, 97, 57, 57, - 57, 57, 2, 57, 57, 2, 2, 2, 2, 2, 57, 57, 57, 57, 57, 57, - 57, 57, 2, 57, 57, 57, 2, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 2, 2, 57, 57, - 57, 2, 2, 2, 2, 57, 57, 2, 2, 2, 2, 2, 2, 2, 88, 88, - 88, 88, 88, 88, 88, 88,117,117,117,117,117,117,117,117,112,112, - 112,112,112,112,112,112,112,112,112,112,112,112,112, 2, 2, 2, - 2,112,112,112,112,112, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 2, 2, 2, 78, 78, 78, 78, 78, 78, 78, 83, 83, - 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 2, 2, 82, 82, - 82, 82, 82, 82, 82, 82, 82, 82, 82, 2, 2, 2, 2, 2,122,122, - 122,122,122,122,122,122,122,122, 2, 2, 2, 2, 2, 2, 2,122, - 122,122,122, 2, 2, 2, 2,122,122,122,122,122,122,122, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 2, 2, 2, 2, 2, 2, 2,130,130, - 130,130,130,130,130,130,130,130,130, 2, 2, 2, 2, 2, 2, 2, - 130,130,130,130,130,130,144,144,144,144,144,144,144,144,144,144, - 2, 2, 2, 2, 2, 2,165,165,165,165,165,165,165,165,165,165, - 165,165,165,165, 2, 2, 2,165,165,165,165,165,165,165, 2, 2, - 2, 2, 2, 2,165,165,156,156,156,156,156,156,156,156,156,156, - 2,156,156,156, 2, 2,156,156, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,147,147, - 147,147,147,147,147,147,148,148,148,148,148,148,148,148,148,148, - 2, 2, 2, 2, 2, 2,158,158,158,158,158,158,158,158,158,158, - 2, 2, 2, 2, 2, 2,153,153,153,153,153,153,153,153,153,153, - 153,153, 2, 2, 2, 2,149,149,149,149,149,149,149,149,149,149, - 149,149,149,149,149, 2, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, - 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 2, 2, - 2, 2, 2, 2, 2, 94, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 85, 2, 2,101,101, - 101,101,101,101,101,101,101, 2, 2, 2, 2, 2, 2, 2,101,101, - 2, 2, 2, 2, 2, 2, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 2, 96, 96,111,111,111,111,111,111,111,111,111,111, - 111,111,111,111,111, 2,100,100,100,100,100,100,100,100, 2, 36, - 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2,108,108, - 108,108,108,108,108,108,108,108, 2,108,108,108,108,108,108,108, - 2, 2, 2, 2, 2, 2,129,129,129,129,129,129,129, 2,129, 2, - 129,129,129,129, 2,129,129,129,129,129,129,129,129,129,129,129, - 129,129,129,129, 2,129,129,129, 2, 2, 2, 2, 2, 2,109,109, - 109,109,109,109,109,109,109,109,109, 2, 2, 2, 2, 2,109,109, - 2, 2, 2, 2, 2, 2,107,107,107,107, 2,107,107,107,107,107, - 107,107,107, 2, 2,107,107, 2, 2,107,107,107,107,107,107,107, - 107,107,107,107,107,107,107, 2,107,107,107,107,107,107,107, 2, - 107,107, 2,107,107,107,107,107, 2, 1,107,107,107,107,107, 2, - 2,107,107,107, 2, 2,107, 2, 2, 2, 2, 2, 2,107, 2, 2, - 2, 2, 2,107,107,107,107,107,107,107, 2, 2,107,107,107,107, - 107,107,107, 2, 2, 2,171,171,171,171,171,171,171,171,171,171, - 2,171, 2, 2,171, 2,171,171,171,171,171,171, 2,171,171, 2, - 171, 2, 2,171, 2,171,171,171,171, 2,171,171,171,171,171, 2, - 2, 2, 2, 2, 2, 2, 2,171,171, 2, 2, 2, 2, 2,137,137, - 137,137,137,137,137,137,137,137,137,137, 2,137,137,137,137,137, - 2, 2, 2, 2, 2, 2,124,124,124,124,124,124,124,124,124,124, - 2, 2, 2, 2, 2, 2,123,123,123,123,123,123,123,123,123,123, - 123,123,123,123, 2, 2,114,114,114,114,114,114,114,114,114,114, - 114,114,114, 2, 2, 2,114,114, 2, 2, 2, 2, 2, 2, 32, 32, - 32, 32, 32, 2, 2, 2,102,102,102,102,102,102,102,102,102,102, - 2, 2, 2, 2, 2, 2, 33, 33, 33, 33, 2, 2, 2, 2,126,126, - 126,126,126,126,126,126,126,126,126, 2, 2,126,126,126,126,126, - 126,126, 2, 2, 2, 2,126,126,126,126,126,126,126, 2,142,142, - 142,142,142,142,142,142,142,142,142,142, 2, 2, 2, 2,125,125, - 125,125,125,125,125,125,125,125,125, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2,125,154,154,154,154,154,154,154, 2, 2,154, - 2, 2,154,154,154,154,154,154,154,154, 2,154,154, 2,154,154, - 154,154,154,154,154,154,154,154,154,154,154,154, 2,154,154, 2, - 2,154,154,154,154,154,154,154, 2, 2, 2, 2, 2, 2,150,150, - 150,150,150,150,150,150, 2, 2,150,150,150,150,150,150,150,150, - 150,150,150, 2, 2, 2,141,141,141,141,141,141,141,141,140,140, - 140,140,140,140,140,140,140,140,140, 2, 2, 2, 2, 2,121,121, - 121,121,121,121,121,121,121, 2, 2, 2, 2, 2, 2, 2, 7, 7, - 2, 2, 2, 2, 2, 2,169,169,169,169,169,169,169,169,169,169, - 2, 2, 2, 2, 2, 2,133,133,133,133,133,133,133,133,133, 2, - 133,133,133,133,133,133,133,133,133,133,133,133,133, 2,133,133, - 133,133,133,133, 2, 2,133,133,133,133,133, 2, 2, 2,134,134, - 134,134,134,134,134,134, 2, 2,134,134,134,134,134,134, 2,134, - 134,134,134,134,134,134,134,134,134,134,134,134,134, 2,138,138, - 138,138,138,138,138, 2,138,138, 2,138,138,138,138,138,138,138, - 138,138,138,138,138,138, 2, 2,138, 2,138,138, 2,138,138,138, - 2, 2, 2, 2, 2, 2,143,143,143,143,143,143, 2,143,143, 2, - 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, - 143,143,143,143,143, 2,143,143, 2,143,143,143,143,143,143, 2, - 2, 2, 2, 2, 2, 2,143,143, 2, 2, 2, 2, 2, 2,145,145, - 145,145,145,145,145,145,145, 2, 2, 2, 2, 2, 2, 2,163,163, - 163,163,163,163,163,163,163, 2,163,163,163,163,163,163,163,163, - 163, 2, 2, 2,163,163,163,163,163, 2, 2, 2, 2, 2, 86, 2, - 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 22, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 2, 63, 63, - 63, 63, 63, 2, 2, 2, 63, 63, 63, 63, 2, 2, 2, 2,157,157, - 157,157,157,157,157,157,157,157,157, 2, 2, 2, 2, 2, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 2, 2, 80, 80, - 80, 2, 2, 2, 2, 2,127,127,127,127,127,127,127,127,127,127, - 127,127,127,127,127, 2,166,166,166,166,166,166,166,166,166,166, - 2, 2, 2, 2, 2, 2, 79, 2, 2, 2, 2, 2, 2, 2,115,115, - 115,115,115,115,115,115,115,115,115,115,115,115,115, 2,115,115, - 2, 2, 2, 2,115,115,159,159,159,159,159,159,159,159,159,159, - 159,159,159,159,159, 2,159,159, 2, 2, 2, 2, 2, 2,103,103, - 103,103,103,103,103,103,103,103,103,103,103,103, 2, 2,119,119, - 119,119,119,119,119,119,119,119,119,119,119,119, 2, 2,119,119, - 2,119,119,119,119,119, 2, 2, 2, 2, 2,119,119,119,167,167, - 167,167,167,167,167,167,167,167, 2, 2, 2, 2, 2, 2,146,146, - 146,146,146,146,146,146,146,146,146, 2, 2, 2, 2, 2, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, 2, 99, 2, 2, - 2, 2, 2, 2, 2, 99,136,139, 13, 13,155, 2, 2, 2,136,136, - 136,136,136,136,136,136,155,155,155,155,155,155,155,155,155,155, - 155,155,155,155, 2, 2, 2, 2, 2, 2, 2, 2, 2,155,136, 2, - 2, 2, 2, 2, 2, 2, 17, 17, 17, 17, 2, 17, 17, 17, 17, 17, - 17, 17, 2, 17, 17, 2, 17, 15, 15, 15, 15, 15, 15, 15, 17, 17, - 17, 2, 2, 2, 2, 2, 2, 2, 15, 2, 2, 2, 2, 2, 15, 15, - 15, 2, 2, 17, 2, 2, 2, 2, 2, 2, 17, 17, 17, 17,139,139, - 139,139,139,139,139,139,139,139,139,139, 2, 2, 2, 2,105,105, - 105,105,105,105,105,105,105,105,105, 2, 2, 2, 2, 2,105,105, - 105,105,105, 2, 2, 2,105, 2, 2, 2, 2, 2, 2, 2,105,105, - 2, 2,105,105,105,105, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 2, - 0, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, - 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, - 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, - 0, 0, 0, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, - 0, 0, 0, 0, 0, 0,131,131,131,131,131,131,131,131,131,131, - 131,131, 2, 2, 2, 2, 2, 2, 2,131,131,131,131,131, 2,131, - 131,131,131,131,131,131, 2, 2, 2, 2, 2, 19, 19, 19, 56, 56, - 56, 56, 56, 56, 56, 2, 56, 2, 2, 56, 56, 56, 56, 56, 56, 56, - 2, 56, 56, 2, 56, 56, 56, 56, 56, 2, 2, 2, 2, 2, 6, 6, - 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6,151,151, - 151,151,151,151,151,151,151,151,151,151,151, 2, 2, 2,151,151, - 151,151,151,151, 2, 2,151,151, 2, 2, 2, 2,151,151,160,160, - 160,160,160,160,160,160,160,160,160,160,160,160,160, 2,152,152, - 152,152,152,152,152,152,152,152, 2, 2, 2, 2, 2,152,164,164, - 164,164,164,164,164,164,164,164, 2, 2, 2, 2, 2, 2,168,168, - 168,168,168,168,168,168,168,168,168, 2, 2, 2, 2,168, 30, 30, - 30, 30, 2, 30, 30, 2,113,113,113,113,113,113,113,113,113,113, - 113,113,113, 2, 2,113,113,113,113,113,113,113,113, 2,132,132, - 132,132,132,132,132,132,132,132,132,132, 2, 2, 2, 2,132,132, - 2, 2, 2, 2,132,132, 3, 3, 3, 3, 2, 3, 3, 3, 2, 3, - 3, 2, 3, 2, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 2, 2, 2, 2, 2, 2, - 3, 2, 2, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 2, 3, - 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 3, - 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, - 3, 3, 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 0, 0, 15, 0, 0, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, - 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 13, 2, - 2, 2, 2, 2, 2, 2, 13, 13, 13, 2, 2, 2, 2, 2, 2, 0, - 2, 2, 2, 2, 2, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 9, 9, 9, 10, 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, + 17, 0, 2, 26, 26, 26, 26, 26, 26, 26, 2, 12, 12, 12, 12, 12, + 12, 2, 12, 12, 12, 0, 39, 39, 39, 39, 39, 2, 2, 2, 39, 39, + 39, 2, 86, 86, 86, 86, 77, 77, 77, 77, 79, 79, 79, 79, 2, 19, + 19, 19, 60, 60, 60, 60, 60, 2, 2, 2, 65, 65, 65, 65, 75, 75, + 75, 75, 75, 75, 2, 2, 2, 2, 75, 75, 69, 69, 69, 69, 69, 69, + 0, 69, 74, 74, 74, 74, 2, 2, 2, 74, 12, 2, 2, 2, 84, 84, + 84, 84, 84, 84, 2, 0, 84, 84, 2, 2, 2, 2, 84, 84, 33, 33, + 33, 2, 68, 68, 68, 68, 68, 68, 68, 2, 68, 68, 2, 2, 92, 92, + 92, 92, 92, 92, 92, 2, 2, 2, 2, 92, 87, 87, 87, 87, 87, 87, + 87, 2, 19, 9, 19, 19, 19, 19, 0, 0, 87, 87, 2, 2, 2, 2, + 2, 12, 19, 19, 19, 2, 2, 2, 2, 4, 14, 2, 14, 2, 14, 14, + 2, 14, 14, 2, 14, 14, 3, 3, 0, 0, 1, 1, 6, 6, 3, 2, + 3, 3, 3, 2, 2, 0, 2, 0, 0, 0, 0, 0, 17, 17, 17, 17, + 0, 0, 2, 2, 12, 12, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, + 49, 2, 49, 49, 2, 49, 49, 49, 2, 2, 0, 2, 2, 2, 9, 2, + 2, 2, 0, 1, 2, 2, 71, 71, 71, 71, 71, 2, 2, 2, 67, 67, + 67, 67, 67, 2, 2, 2, 42, 42, 42, 42, 2, 42, 42, 42, 41, 41, + 41, 41, 41, 41, 41, 2,118,118,118,118,118,118,118, 2, 53, 53, + 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, + 40, 40, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 2, 2,135,135, + 135,135,106,106,106,106,104,104,104,104, 2, 2, 2,104,161,161, + 161,161,161,161,161, 2,161,161, 2,161,161, 2, 2, 2,170,170, + 170,170,110,110,110,110,110,110,110, 2,110,110, 2, 2, 19, 19, + 2, 19, 19, 2, 19, 19, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, + 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, 2, 47, 81, 81, + 81, 81, 81, 81, 2, 81,120,120,120,120,116,116,116,116,116,116, + 116, 2, 2, 2, 2,116,128,128,128,128,128,128,128, 2,128,128, + 2, 2, 2, 2, 2,128, 66, 66, 66, 66, 2, 2, 2, 66, 72, 72, + 72, 72, 72, 72, 2, 2, 2, 2, 2, 72,173,173,173,173,173,173, + 2, 2, 98, 98, 98, 98, 97, 97, 97, 97, 2, 2, 97, 97, 57, 57, + 57, 57, 2, 57, 57, 2, 2, 57, 57, 57, 57, 57, 2, 2, 57, 57, + 57, 2, 2, 2, 2, 57, 57, 2, 2, 2, 88, 88, 88, 88,117,117, + 117,117,112,112,112,112,112,112,112, 2, 2, 2, 2,112, 78, 78, + 78, 78, 78, 78, 2, 2, 2, 78, 78, 78, 83, 83, 83, 83, 83, 83, + 2, 2, 82, 82, 82, 82, 82, 82, 82, 2,122,122,122,122,122,122, + 2, 2, 2,122,122,122,122, 2, 2, 2, 89, 89, 89, 89, 89, 2, + 2, 2,130,130,130,130,130,130,130, 2, 2, 2,130,130,144,144, + 144,144,144,144, 2, 2,165,165,165,165,165,165, 2, 2, 2,165, + 165,165, 2, 2,165,165, 3, 3, 3, 2,156,156,156,156,156,156, + 2,156,156,156, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2,147,147, + 147,147,148,148,148,148,148,148, 2, 2,158,158,158,158,158,158, + 2, 2,153,153,153,153,149,149,149,149,149,149,149, 2, 94, 94, + 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 2, 2, 2, 94, 85, 85, + 85, 85, 85, 85, 85, 2, 2, 85, 2, 2,101,101,101,101,101, 2, + 2, 2,101,101, 2, 2, 96, 96, 96, 96, 96, 2, 96, 96,111,111, + 111,111,111,111,111, 2,100,100,100,100,108,108,108,108,108,108, + 2,108,108,108, 2, 2,129,129,129,129,129,129,129, 2,129, 2, + 129,129,129,129, 2,129,129,129, 2, 2,109,109,109,109,109,109, + 109, 2,109,109, 2, 2,107,107,107,107, 2,107,107,107,107, 2, + 2,107,107, 2,107,107,107,107, 2, 1,107,107, 2, 2,107, 2, + 2, 2, 2, 2, 2,107, 2, 2,107,107,171,171,171,171,171,171, + 2,171, 2, 2,171, 2,171, 2,171, 2, 2,171, 2,171,171,171, + 171, 2,171, 2, 2, 2, 2,171,171, 2,137,137,137,137, 2,137, + 137,137,137,137, 2, 2,124,124,124,124,124,124, 2, 2,123,123, + 123,123,123,123, 2, 2,114,114,114,114,114, 2, 2, 2,114,114, + 2, 2,102,102,102,102,102,102, 2, 2,126,126,126,126,126,126, + 126, 2, 2,126,126,126,142,142,142,142,125,125,125,125,125,125, + 125, 2, 2, 2, 2,125,154,154,154,154,154,154,154, 2, 2,154, + 2, 2, 2,154,154, 2,154,154, 2,154,154, 2, 2,154,154,154, + 2, 2,150,150,150,150, 2, 2,150,150,150, 2, 2, 2,141,141, + 141,141,140,140,140,140,140,140,140, 2,121,121,121,121,121, 2, + 2, 2, 7, 7, 2, 2,169,169,169,169,169,169, 2, 2,133,133, + 133,133,133, 2,133,133,133,133,133, 2,133,133, 2, 2,133, 2, + 2, 2,134,134,134,134, 2, 2,134,134, 2,134,134,134,134,134, + 134, 2,138,138,138,138,138,138,138, 2,138,138, 2,138, 2, 2, + 138, 2,138,138, 2, 2,143,143,143,143,143,143, 2,143,143, 2, + 143,143,143,143,143, 2,143, 2, 2, 2,143,143, 2, 2,175,175, + 175,175,175,175, 2, 2,145,145,145,145,145, 2, 2, 2,163,163, + 163,163,163, 2,163,163,163,163,163, 2, 2, 2,163,163, 86, 2, + 2, 2, 63, 63, 63, 63, 63, 63, 2, 2, 63, 63, 63, 2, 63, 2, + 2, 2,157,157,157,157,157,157,157, 2, 80, 80, 80, 80, 80, 80, + 2, 2, 80, 80, 80, 2,127,127,127,127,127,127,127, 2,166,166, + 166,166,166,166, 2, 2, 79, 2, 2, 2,115,115,115,115,115,115, + 115, 2,115,115, 2, 2, 2, 2,115,115,159,159,159,159,159,159, + 159, 2,159,159, 2, 2,103,103,103,103,103,103, 2, 2,119,119, + 119,119,119,119, 2, 2,119,119, 2,119, 2,119,119,119,167,167, + 167,167,167,167, 2, 2,146,146,146,146,146,146,146, 2,172,172, + 172,172,172, 2, 2,172, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, + 2, 99,136,139, 13, 13,155, 2, 2, 2, 13, 13, 13, 2,136,136, + 136,136,155,155,155,155,155,155, 2, 2, 2, 2, 2,155,136,136, + 136, 2, 2, 17, 17, 17, 2, 17, 17, 2, 17, 15, 15, 15, 17, 17, + 17, 2, 2, 2, 15, 2, 2, 17, 2, 2,139,139,139,139,105,105, + 105,105,105,105,105, 2,105, 2, 2, 2,105,105, 2, 2, 1, 1, + 1, 2, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 2, 2, + 0, 2, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 2,131,131, + 131,131, 2, 2, 2,131, 2,131,131,131, 56, 56, 56, 2, 56, 2, + 2, 56, 56, 56, 2, 56, 56, 2, 56, 56, 6, 6, 2, 2, 2, 2, + 2, 6,151,151,151,151,151, 2, 2, 2,151,151, 2, 2, 2, 2, + 151,151,160,160,160,160,160,160,160, 2,152,152,152,152,152,152, + 2, 2, 2, 2, 2,152,164,164,164,164,164,164, 2, 2,168,168, + 168,168,168,168,168, 2, 2, 2, 2,168,174,174,174,174,174,174, + 174, 2,174,174, 2, 2, 2, 2,174,174, 2, 30, 30, 2,113,113, + 113,113,113, 2, 2,113,113,113,113, 2,132,132,132,132,132,132, + 2, 2, 2, 2,132,132, 2, 3, 3, 3, 2, 3, 3, 2, 3, 2, + 2, 3, 2, 3, 2, 3, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, + 2, 3, 15, 0, 0, 2, 0, 2, 2, 0, 13, 2, 2, 2, 2, 0, + 2, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, + 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 16, 17, 9, 9, 9, 9, 18, 9, 9, 9, 9, 9, 19, 20, - 21, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 23, 9, 9, 9, 9, 9, 24, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, + 9, 9, 9, 9, 18, 9, 9, 9, 9, 9, 19, 20, 21, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 9, + 9, 9, 23, 9, 9, 9, 9, 9, 24, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -3933,67 +3605,65 @@ _hb_ucd_u8[17524] = 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, - 29, 30, 0, 0, 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, - 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 41, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 26, 27, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, + 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, - 46, 47, 0, 0, 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, - 53, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, - 55, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, - 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, - 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, + 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, + 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, + 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, + 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, - 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, - 99,100,101,102,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0, - 107, 0, 0, 0,108, 0,109, 0,110, 0,111,112,113, 0,114, 0, - 0, 0,115, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102, + 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0,107, 0, 0, 0, + 108, 0,109, 0,110, 0,111,112,113, 0,114, 0, 0, 0,115, 0, + 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,118,119,120,121, 0,122,123,124, - 125,126, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,128,129,130,131,132,133,134,135,136,137,138,139, - 140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155, - 156,157, 0, 0, 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,118,119,120,121, 0,122,123,124,125,126, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 162, 0,163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,164,165, 0, - 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151,152,153,154,155,156,157, 0, 0, + 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,167, 0, 0, 0,168,169, 0, 0, - 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,171, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,172, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,162, 0,163, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,164,165, 0, 0, 0, 0, 0, + 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,173, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,167, 0, 0, 0,168,169, 0, 0,170, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,171, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,174, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,175, 0, 0, 0, 0, 0, + 0, 0,174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,176,177, 0, 0, 0, 0,178, - 179, 0, 0, 0,180,181,182,183,184,185,186,187,188,189,190,191, - 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, - 208,209,210,211,212,213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 2, 3, 4, + 0, 0, 0, 0, 0,176,177, 0, 0, 0, 0,178,179, 0, 0, 0, + 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195, + 196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211, + 212,213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, }; -static const uint16_t -_hb_ucd_u16[9668] = +static const uint16_t _hb_ucd_u16[10904]= { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, @@ -4020,23 +3690,23 @@ _hb_ucd_u16[9668] = 48, 184, 48, 185, 48, 186, 187, 188, 48, 48, 48, 189, 190, 191, 192, 193, 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, 48, 200, 201, 202, 203, 48, 204, 205, 48, 48, 206, 48, 207, 208, 209, 209, - 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 216, 140, 140, 140, - 217, 48, 48, 218, 219, 160, 220, 221, 222, 48, 223, 64, 48, 48, 224, 225, - 48, 48, 226, 227, 228, 64, 48, 229, 230, 9, 9, 231, 232, 233, 234, 235, - 11, 11, 236, 27, 27, 27, 237, 238, 11, 239, 27, 27, 32, 32, 32, 32, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 240, 13, 13, 13, 13, 13, 13, - 241, 242, 241, 241, 242, 243, 241, 244, 245, 245, 245, 246, 247, 248, 249, 250, - 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 261, 262, 263, 264, 265, - 266, 267, 268, 269, 270, 271, 272, 272, 273, 274, 275, 209, 276, 277, 209, 278, - 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, - 280, 209, 281, 209, 209, 209, 209, 282, 209, 283, 279, 284, 209, 285, 286, 209, - 209, 209, 176, 140, 287, 140, 271, 271, 271, 288, 209, 209, 209, 209, 289, 271, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 290, 291, 209, 209, 292, - 209, 209, 209, 209, 209, 209, 293, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 294, 295, 271, 296, 209, 209, 297, 279, 298, 279, + 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 32, 216, 217, 140, + 218, 48, 48, 219, 220, 160, 221, 222, 223, 48, 224, 64, 48, 48, 225, 226, + 48, 48, 227, 228, 229, 64, 48, 230, 231, 9, 9, 232, 233, 234, 235, 236, + 11, 11, 237, 27, 27, 27, 238, 239, 11, 240, 27, 27, 32, 32, 32, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 241, 13, 13, 13, 13, 13, 13, + 242, 243, 242, 242, 243, 244, 242, 245, 246, 246, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 273, 274, 275, 276, 209, 277, 278, 209, 279, + 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 281, 209, 282, 209, 209, 209, 209, 283, 209, 284, 280, 285, 209, 286, 287, 209, + 209, 209, 176, 140, 288, 140, 272, 272, 272, 289, 209, 209, 209, 209, 290, 272, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 291, 292, 209, 209, 293, + 209, 209, 209, 209, 209, 209, 294, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 295, 296, 272, 297, 209, 209, 298, 280, 299, 280, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 279, 279, 279, 279, 279, 279, 279, 279, 299, 300, 279, 279, 279, 301, 279, 302, - 209, 209, 209, 279, 303, 209, 209, 304, 209, 305, 209, 209, 209, 209, 209, 209, + 280, 280, 280, 280, 280, 280, 280, 280, 300, 301, 280, 280, 280, 302, 280, 303, + 209, 209, 209, 280, 304, 209, 209, 305, 209, 209, 209, 209, 209, 209, 209, 209, 9, 9, 9, 11, 11, 11, 306, 307, 13, 13, 13, 13, 13, 13, 308, 309, 11, 11, 310, 48, 48, 48, 311, 312, 48, 313, 314, 314, 314, 314, 32, 32, 315, 316, 317, 318, 319, 320, 140, 140, 209, 321, 209, 209, 209, 209, 209, 322, @@ -4046,7 +3716,7 @@ _hb_ucd_u16[9668] = 209, 333, 334, 209, 335, 336, 209, 209, 334, 209, 209, 336, 209, 209, 209, 209, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 209, 209, 209, 209, 48, 337, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 338, 48, 48, 229, + 48, 48, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 338, 48, 48, 230, 339, 48, 340, 140, 13, 13, 341, 342, 13, 343, 48, 48, 48, 48, 344, 345, 31, 346, 347, 348, 13, 13, 13, 349, 350, 351, 352, 353, 354, 355, 140, 356, 357, 48, 358, 359, 48, 48, 48, 360, 361, 48, 48, 362, 363, 192, 32, 364, @@ -4062,1560 +3732,407 @@ _hb_ucd_u16[9668] = 32, 404, 32, 405, 406, 407, 408, 409, 48, 48, 48, 48, 48, 48, 48, 410, 411, 2, 3, 4, 5, 412, 413, 414, 48, 415, 48, 200, 416, 417, 418, 419, 420, 48, 172, 421, 204, 204, 140, 140, 48, 48, 48, 48, 48, 48, 48, 71, - 422, 271, 271, 423, 272, 272, 272, 424, 425, 426, 427, 140, 140, 209, 209, 428, + 422, 272, 272, 423, 273, 273, 273, 424, 425, 426, 427, 140, 140, 209, 209, 428, 140, 140, 140, 140, 140, 140, 140, 140, 48, 151, 48, 48, 48, 100, 429, 430, 48, 48, 431, 48, 432, 48, 48, 433, 48, 434, 48, 48, 435, 436, 140, 140, 9, 9, 437, 11, 11, 48, 48, 48, 48, 204, 192, 9, 9, 438, 11, 439, 48, 48, 440, 48, 48, 48, 441, 442, 442, 443, 444, 445, 48, 48, 48, 388, 48, 48, 48, 313, 48, 199, 440, 140, 446, 27, 27, 447, 140, 140, 140, 140, 448, 48, 48, 449, 48, 450, 48, 451, 48, 200, 452, 140, 140, 140, 48, 453, - 48, 454, 48, 455, 140, 140, 140, 140, 48, 48, 48, 456, 271, 457, 271, 271, + 48, 454, 48, 455, 48, 207, 140, 140, 48, 48, 48, 456, 272, 457, 272, 272, 458, 459, 48, 460, 461, 462, 48, 463, 48, 464, 140, 140, 465, 48, 466, 467, 48, 48, 48, 468, 48, 469, 48, 470, 48, 471, 472, 140, 140, 140, 140, 140, 48, 48, 48, 48, 196, 140, 140, 140, 9, 9, 9, 473, 11, 11, 11, 474, 48, 48, 475, 192, 476, 9, 477, 11, 478, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 271, 479, 48, 48, 480, 481, 482, 140, 140, 483, - 48, 464, 484, 48, 62, 485, 140, 48, 486, 140, 140, 48, 487, 140, 48, 313, - 488, 48, 48, 489, 490, 457, 491, 492, 222, 48, 48, 493, 494, 48, 196, 192, - 495, 48, 496, 497, 498, 48, 48, 499, 222, 48, 48, 500, 501, 502, 503, 504, - 48, 97, 505, 506, 507, 140, 140, 140, 508, 509, 510, 48, 48, 511, 512, 192, - 513, 83, 84, 514, 515, 516, 517, 518, 519, 48, 48, 520, 521, 522, 523, 140, - 48, 48, 48, 524, 525, 526, 481, 140, 48, 48, 48, 527, 528, 192, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 529, 530, 531, 532, 140, 140, - 48, 48, 48, 533, 534, 192, 535, 140, 48, 48, 536, 537, 192, 538, 539, 140, - 48, 540, 541, 542, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 505, 543, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 544, - 545, 546, 48, 547, 548, 192, 140, 140, 140, 140, 549, 48, 48, 550, 551, 140, - 552, 48, 48, 553, 554, 555, 48, 48, 556, 557, 558, 48, 48, 48, 48, 196, - 559, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 560, 192, - 84, 48, 529, 561, 562, 148, 175, 563, 48, 564, 565, 566, 140, 140, 140, 140, - 567, 48, 48, 568, 569, 192, 570, 48, 571, 572, 192, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 573, - 574, 115, 48, 575, 576, 577, 140, 140, 140, 140, 140, 100, 271, 578, 579, 580, + 140, 140, 140, 140, 140, 140, 272, 479, 48, 48, 480, 481, 482, 483, 140, 484, + 48, 464, 485, 48, 62, 486, 140, 48, 487, 140, 140, 48, 488, 140, 48, 313, + 489, 48, 48, 490, 491, 457, 492, 493, 223, 48, 48, 494, 495, 48, 196, 192, + 496, 48, 497, 498, 499, 48, 48, 500, 223, 48, 48, 501, 502, 503, 504, 505, + 48, 97, 506, 507, 508, 140, 140, 140, 509, 510, 511, 48, 48, 512, 513, 192, + 514, 83, 84, 515, 516, 517, 518, 519, 520, 48, 48, 521, 522, 523, 524, 140, + 48, 48, 48, 525, 526, 527, 481, 140, 48, 48, 48, 528, 529, 192, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 530, 531, 532, 533, 140, 140, + 48, 48, 48, 534, 535, 192, 536, 140, 48, 48, 537, 538, 192, 539, 540, 140, + 48, 541, 542, 543, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 506, 544, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 545, + 546, 547, 48, 548, 549, 192, 140, 140, 140, 140, 550, 48, 48, 551, 552, 140, + 553, 48, 48, 554, 555, 556, 48, 48, 557, 558, 559, 48, 48, 48, 48, 196, + 560, 140, 140, 140, 140, 140, 561, 140, 140, 140, 140, 140, 48, 48, 562, 192, + 84, 48, 530, 563, 564, 148, 175, 565, 48, 566, 567, 568, 140, 140, 140, 140, + 569, 48, 48, 570, 571, 192, 572, 48, 573, 574, 192, 48, 48, 575, 192, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 576, + 577, 115, 48, 578, 579, 580, 140, 140, 140, 140, 140, 100, 272, 581, 582, 583, 48, 48, 48, 48, 48, 48, 48, 48, 48, 207, 140, 140, 140, 140, 140, 140, - 272, 272, 272, 272, 272, 272, 581, 582, 48, 48, 48, 48, 48, 48, 48, 48, + 273, 273, 273, 273, 273, 273, 584, 585, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 388, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 583, - 48, 48, 48, 584, 585, 586, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 586, + 48, 48, 48, 587, 588, 589, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 71, 48, 48, 48, 48, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 587, 588, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 589, - 48, 48, 48, 590, 591, 592, 593, 594, 48, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 595, 48, 596, 192, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 9, 9, 11, 11, 271, 597, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 48, 598, 599, 600, 600, 601, 602, 140, 140, 140, 140, 603, 604, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 440, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 199, 140, 605, - 196, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 606, - 48, 48, 607, 608, 140, 609, 610, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 590, 591, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 592, + 48, 48, 48, 593, 594, 595, 596, 597, 48, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 598, 48, 599, 192, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 9, 9, 11, 11, 272, 600, 9, 601, 11, 602, 140, 140, + 48, 48, 48, 48, 603, 604, 605, 605, 606, 607, 140, 140, 140, 140, 608, 609, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 199, 140, 610, + 48, 200, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 48, 611, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 612, + 48, 48, 611, 613, 140, 614, 615, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 206, - 48, 48, 48, 48, 48, 48, 71, 151, 196, 611, 612, 140, 140, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 192, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, 140, - 32, 32, 613, 32, 614, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, + 48, 48, 48, 48, 48, 48, 71, 151, 196, 616, 617, 140, 140, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 618, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 619, 209, 427, 209, 620, + 32, 32, 216, 32, 621, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 323, - 209, 209, 615, 209, 209, 209, 616, 617, 618, 209, 619, 209, 209, 209, 287, 140, - 209, 209, 209, 209, 620, 140, 140, 140, 140, 140, 140, 140, 271, 621, 271, 621, - 209, 209, 209, 209, 209, 338, 271, 461, 140, 140, 140, 140, 140, 140, 140, 140, - 9, 622, 11, 623, 624, 625, 241, 9, 626, 627, 628, 629, 630, 9, 622, 11, - 631, 632, 11, 633, 634, 635, 636, 9, 637, 11, 9, 622, 11, 623, 624, 11, - 241, 9, 626, 636, 9, 637, 11, 9, 622, 11, 638, 9, 639, 640, 641, 642, - 11, 643, 9, 644, 645, 646, 647, 11, 648, 9, 649, 11, 650, 538, 538, 538, - 32, 32, 32, 651, 32, 32, 652, 653, 654, 655, 45, 140, 140, 140, 140, 140, - 656, 657, 658, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 659, 660, 661, 27, 27, 27, 662, 140, 663, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 151, 664, 665, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 666, 140, 48, 48, 667, 668, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 669, 192, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 587, 670, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 671, 200, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 672, 614, 140, 140, - 9, 9, 626, 11, 673, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 503, 271, 271, 674, 675, 140, 140, 140, 140, - 503, 271, 676, 677, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 678, 48, 679, 680, 681, 682, 683, 684, 685, 206, 686, 206, 140, 140, 140, 687, - 209, 209, 688, 209, 209, 209, 209, 209, 209, 322, 333, 689, 689, 689, 209, 323, - 690, 209, 209, 209, 209, 209, 209, 209, 209, 209, 691, 140, 140, 140, 692, 209, - 693, 209, 209, 688, 694, 695, 323, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 696, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 697, 426, 426, - 209, 209, 209, 209, 209, 209, 209, 698, 209, 209, 209, 209, 209, 176, 688, 427, - 688, 209, 209, 209, 699, 176, 209, 209, 699, 209, 691, 688, 695, 140, 140, 140, - 209, 209, 209, 209, 209, 322, 691, 426, 700, 209, 209, 209, 701, 702, 176, 694, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 703, 209, 209, 209, 209, 209, 192, + 209, 209, 622, 209, 209, 209, 623, 624, 625, 209, 626, 209, 209, 209, 288, 140, + 209, 209, 209, 209, 627, 140, 140, 140, 140, 140, 140, 140, 272, 628, 272, 628, + 209, 209, 209, 209, 209, 338, 272, 461, 140, 140, 140, 140, 140, 140, 140, 140, + 9, 629, 11, 630, 631, 632, 242, 9, 633, 634, 635, 636, 637, 9, 629, 11, + 638, 639, 11, 640, 641, 642, 643, 9, 644, 11, 9, 629, 11, 630, 631, 11, + 242, 9, 633, 643, 9, 644, 11, 9, 629, 11, 645, 9, 646, 647, 648, 649, + 11, 650, 9, 651, 652, 653, 654, 11, 655, 9, 656, 11, 657, 539, 539, 539, + 32, 32, 32, 658, 32, 32, 659, 660, 661, 662, 45, 140, 140, 140, 140, 140, + 663, 664, 665, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 666, 667, 668, 27, 27, 27, 669, 140, 670, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 151, 671, 672, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 673, 140, 48, 48, 674, 675, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 676, 192, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 590, 677, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 200, 678, 679, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 680, 200, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 681, 621, 140, 140, + 9, 9, 633, 11, 682, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 504, 272, 272, 683, 684, 140, 140, 140, 140, + 504, 272, 685, 686, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 687, 48, 688, 689, 690, 691, 692, 693, 694, 206, 695, 206, 140, 140, 140, 696, + 209, 209, 697, 209, 209, 209, 209, 209, 209, 322, 333, 698, 698, 698, 209, 323, + 699, 209, 209, 209, 209, 209, 209, 209, 209, 209, 700, 140, 140, 140, 701, 209, + 702, 209, 209, 697, 703, 704, 323, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 705, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 706, 426, 426, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 176, 697, 427, + 697, 209, 209, 209, 707, 176, 209, 209, 707, 209, 700, 697, 704, 708, 140, 140, + 209, 209, 209, 209, 209, 707, 700, 426, 709, 209, 209, 209, 710, 711, 712, 703, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 713, 209, 209, 209, 209, 209, 714, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 140, 140, - 48, 48, 48, 207, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 481, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 100, 48, 48, 48, 48, 48, 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 71, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 140, 140, 140, 140, 140, - 704, 140, 584, 584, 584, 584, 584, 584, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 48, 48, 48, 207, 140, 140, 140, 140, 140, 140, 140, 140, + 715, 140, 587, 587, 587, 587, 587, 587, 140, 140, 140, 140, 140, 140, 140, 140, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 140, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 705, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 706, - 0, 0, 1, 1, 0, 2, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 5, 0, 6, 7, 7, 7, 8, 9, 10, 11, 12, - 13, 13, 13, 13, 14, 13, 13, 13, 13, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 23, 23, 26, 23, 27, 28, 29, 23, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 23, 23, 39, 40, 40, 41, 42, 43, 44, 45, 46, - 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, - 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, - 79, 80, 81, 82, 83, 84, 85, 82, 86, 86, 87, 88, 89, 90, 91, 82, - 92, 92, 92, 92, 92, 93, 94, 95, 96, 96, 96, 96, 96, 96, 96, 96, - 97, 97, 98, 97, 99, 100, 101, 97, 102, 97, 103, 104, 105, 106, 106, 107, - 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 109, 110, 110, 111, - 112, 113, 114, 115, 116, 116, 117, 118, 119, 120, 120, 121, 120, 122, 108, 123, - 124, 125, 126, 127, 128, 129, 130, 116, 131, 132, 133, 134, 135, 136, 137, 82, - 138, 138, 139, 138, 140, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, - 4, 151, 152, 153, 4, 154, 7, 7, 155, 11, 156, 157, 11, 158, 159, 160, - 161, 0, 0, 162, 163, 0, 164, 165, 0, 166, 167, 4, 168, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 170, 0, 0, 0, 0, 0, - 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 172, 173, 0, 0, 0, - 174, 174, 174, 4, 175, 175, 175, 176, 93, 177, 178, 179, 180, 181, 181, 13, - 0, 0, 182, 82, 183, 184, 184, 185, 184, 184, 184, 184, 184, 184, 186, 187, - 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 96, 96, 198, 199, 0, 200, - 201, 0, 0, 202, 0, 0, 203, 204, 194, 194, 205, 0, 0, 0, 0, 0, - 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 0, 0, - 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 207, 206, 208, 209, - 210, 210, 210, 210, 210, 210, 210, 210, 210, 211, 13, 13, 13, 212, 212, 213, - 0, 214, 4, 4, 215, 4, 216, 217, 218, 219, 220, 221, 222, 222, 223, 40, - 224, 225, 226, 227, 228, 228, 229, 230, 231, 232, 233, 92, 234, 234, 235, 236, - 237, 238, 239, 240, 106, 106, 241, 242, 96, 96, 96, 96, 96, 243, 244, 245, - 82, 82, 82, 82, 82, 82, 82, 82, 184, 184, 184, 246, 184, 184, 247, 82, - 248, 249, 250, 23, 23, 23, 251, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 252, 23, 23, 253, 23, 254, 255, 256, 257, 258, 259, 23, 23, 23, 260, - 261, 1, 1, 262, 263, 201, 264, 265, 266, 267, 268, 82, 269, 269, 269, 270, - 271, 272, 11, 11, 273, 274, 187, 275, 82, 82, 82, 82, 276, 277, 278, 279, - 280, 281, 282, 283, 284, 285, 286, 82, 287, 287, 288, 289, 290, 291, 292, 293, - 294, 295, 296, 297, 298, 299, 300, 301, 302, 302, 302, 302, 302, 302, 302, 302, - 302, 303, 304, 305, 306, 307, 82, 82, 308, 309, 310, 311, 312, 313, 82, 314, - 315, 316, 82, 82, 317, 318, 319, 320, 321, 322, 323, 324, 325, 82, 326, 327, - 328, 329, 330, 331, 332, 333, 82, 82, 334, 334, 335, 82, 336, 337, 336, 338, - 339, 340, 341, 342, 343, 82, 82, 82, 82, 82, 82, 344, 345, 346, 347, 348, - 349, 350, 351, 352, 353, 354, 355, 356, 357, 357, 358, 359, 360, 360, 361, 362, - 363, 364, 365, 366, 367, 367, 367, 368, 369, 370, 371, 82, 372, 373, 374, 375, - 376, 377, 378, 379, 380, 381, 382, 383, 384, 384, 385, 386, 387, 387, 388, 82, - 82, 82, 82, 82, 389, 390, 391, 82, 392, 392, 393, 394, 395, 396, 397, 398, - 399, 400, 401, 82, 82, 82, 82, 82, 402, 403, 82, 82, 82, 404, 404, 405, - 406, 407, 408, 82, 82, 409, 410, 411, 412, 412, 413, 414, 414, 415, 416, 417, - 418, 82, 82, 82, 82, 82, 419, 420, 421, 422, 423, 424, 425, 426, 82, 82, - 427, 428, 429, 430, 431, 432, 82, 82, 82, 82, 82, 82, 82, 82, 82, 433, - 434, 435, 436, 82, 82, 437, 438, 439, 440, 440, 440, 440, 440, 440, 440, 440, - 440, 440, 440, 440, 441, 82, 82, 82, 440, 440, 440, 442, 440, 440, 440, 440, - 440, 440, 443, 82, 82, 82, 82, 82, 82, 82, 82, 82, 444, 445, 445, 446, - 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 448, 447, 447, 447, 447, 447, - 447, 447, 447, 447, 447, 447, 447, 449, 450, 450, 450, 450, 450, 450, 450, 450, - 450, 450, 451, 82, 82, 82, 82, 82, 452, 453, 82, 82, 82, 82, 82, 82, - 212, 212, 212, 212, 212, 212, 212, 212, 212, 454, 455, 456, 457, 458, 459, 460, - 461, 461, 462, 463, 464, 82, 82, 82, 82, 82, 465, 466, 82, 82, 82, 82, - 82, 82, 467, 467, 468, 82, 82, 82, 469, 469, 470, 469, 471, 82, 82, 472, - 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 474, - 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 476, 477, - 478, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 479, - 480, 191, 191, 191, 191, 191, 191, 191, 191, 481, 482, 483, 484, 484, 484, 484, - 484, 484, 484, 484, 484, 484, 484, 485, 486, 486, 486, 487, 488, 489, 82, 82, - 0, 0, 0, 0, 0, 0, 0, 490, 0, 0, 0, 0, 0, 491, 82, 82, - 7, 492, 493, 0, 0, 0, 489, 82, 0, 0, 0, 0, 0, 0, 0, 494, - 0, 495, 0, 496, 497, 498, 0, 170, 11, 11, 499, 82, 82, 82, 491, 491, - 0, 0, 500, 501, 82, 82, 82, 82, 0, 0, 502, 0, 503, 504, 505, 0, - 506, 507, 508, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 509, 0, 0, - 0, 0, 0, 0, 0, 0, 510, 0, 511, 511, 511, 511, 511, 511, 511, 511, - 511, 511, 511, 511, 512, 513, 82, 82, 514, 515, 82, 82, 82, 82, 82, 82, - 516, 517, 13, 518, 519, 82, 82, 82, 520, 521, 522, 82, 82, 82, 82, 82, - 82, 82, 82, 82, 523, 524, 525, 526, 82, 82, 82, 82, 82, 82, 527, 528, - 82, 82, 82, 82, 82, 82, 529, 530, 82, 82, 82, 82, 82, 82, 82, 531, - 532, 532, 532, 532, 532, 532, 533, 82, 534, 534, 535, 82, 82, 82, 82, 82, - 82, 82, 82, 536, 0, 537, 82, 82, 261, 182, 82, 82, 82, 82, 82, 82, - 538, 539, 540, 541, 542, 543, 82, 544, 0, 545, 0, 0, 491, 546, 547, 494, - 0, 0, 0, 0, 0, 548, 82, 549, 550, 551, 552, 553, 82, 82, 82, 82, - 0, 0, 0, 0, 0, 0, 554, 555, 0, 0, 0, 556, 0, 0, 490, 557, - 545, 0, 558, 0, 559, 560, 561, 82, 0, 0, 491, 562, 563, 0, 564, 565, - 0, 0, 0, 0, 258, 0, 0, 490, 184, 184, 184, 184, 184, 184, 184, 82, - 184, 247, 184, 184, 184, 184, 184, 184, 566, 184, 184, 184, 184, 184, 184, 184, - 184, 184, 184, 184, 184, 567, 184, 184, 184, 184, 184, 184, 184, 184, 184, 568, - 184, 184, 566, 82, 82, 82, 82, 82, 566, 82, 82, 82, 82, 82, 82, 82, - 184, 184, 569, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 570, 82, 82, - 571, 0, 0, 0, 82, 82, 82, 82, 7, 7, 7, 7, 7, 7, 7, 572, - 0, 0, 0, 0, 1, 2, 2, 3, 0, 4, 0, 4, 2, 2, 5, 2, - 2, 2, 2, 2, 2, 2, 2, 6, 7, 8, 0, 0, 9, 9, 9, 9, - 9, 9, 10, 11, 12, 13, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, - 16, 17, 14, 14, 18, 18, 18, 18, 19, 18, 18, 18, 18, 18, 20, 21, - 21, 21, 22, 20, 21, 21, 21, 21, 21, 23, 24, 25, 25, 25, 25, 25, - 25, 26, 25, 25, 25, 27, 28, 26, 29, 30, 31, 32, 31, 31, 31, 31, - 33, 34, 35, 31, 31, 31, 36, 31, 31, 31, 31, 29, 37, 38, 37, 37, - 37, 37, 37, 37, 37, 39, 31, 31, 40, 40, 40, 40, 40, 40, 41, 26, - 42, 42, 42, 42, 42, 42, 42, 43, 44, 44, 44, 44, 44, 45, 44, 46, - 47, 47, 47, 48, 37, 49, 31, 31, 31, 50, 51, 31, 52, 31, 31, 31, - 53, 53, 53, 53, 53, 53, 54, 53, 55, 53, 53, 53, 56, 57, 58, 59, - 59, 60, 61, 62, 57, 63, 64, 65, 66, 59, 59, 67, 68, 69, 70, 71, - 71, 72, 73, 74, 69, 75, 76, 77, 78, 71, 79, 26, 80, 81, 82, 83, - 83, 84, 85, 86, 81, 87, 88, 26, 89, 83, 90, 91, 92, 93, 94, 95, - 95, 96, 97, 98, 93, 99, 100, 101, 102, 95, 95, 26, 103, 104, 105, 106, - 107, 104, 108, 109, 104, 105, 110, 26, 111, 108, 108, 112, 113, 114, 115, 113, - 113, 115, 113, 116, 114, 117, 118, 119, 120, 113, 121, 113, 122, 123, 124, 122, - 122, 124, 125, 126, 123, 127, 128, 128, 129, 122, 130, 26, 131, 132, 133, 131, - 131, 131, 131, 131, 132, 133, 134, 131, 135, 131, 131, 131, 136, 137, 138, 139, - 137, 137, 140, 141, 138, 142, 143, 137, 144, 137, 145, 26, 146, 147, 147, 147, - 147, 147, 147, 148, 147, 147, 147, 149, 26, 26, 26, 26, 150, 151, 152, 152, - 153, 152, 152, 154, 155, 156, 152, 157, 158, 158, 158, 158, 158, 159, 158, 158, - 158, 160, 159, 158, 158, 158, 158, 159, 158, 158, 158, 161, 158, 161, 162, 163, - 164, 164, 164, 164, 165, 165, 165, 165, 166, 167, 165, 165, 165, 165, 165, 168, - 169, 169, 169, 169, 170, 170, 170, 170, 170, 171, 172, 171, 170, 171, 170, 170, - 170, 170, 171, 172, 171, 170, 172, 170, 170, 170, 171, 170, 170, 170, 170, 173, - 170, 170, 170, 174, 170, 170, 170, 175, 176, 176, 176, 176, 176, 176, 177, 177, - 178, 178, 178, 178, 179, 179, 179, 180, 181, 181, 181, 181, 181, 182, 181, 183, - 184, 184, 185, 186, 187, 187, 188, 26, 189, 189, 190, 26, 191, 192, 193, 26, - 194, 194, 194, 194, 194, 194, 194, 195, 194, 196, 194, 196, 197, 198, 198, 199, - 198, 198, 198, 198, 198, 198, 198, 200, 198, 201, 178, 178, 178, 178, 202, 26, - 203, 203, 203, 204, 203, 205, 203, 205, 206, 203, 207, 207, 207, 208, 209, 26, - 210, 210, 210, 210, 210, 211, 210, 210, 210, 212, 210, 213, 214, 214, 214, 215, - 216, 216, 216, 216, 216, 216, 216, 217, 216, 216, 216, 218, 216, 219, 216, 219, - 216, 220, 9, 9, 9, 221, 26, 26, 222, 222, 222, 222, 222, 223, 222, 222, - 224, 224, 224, 224, 225, 225, 225, 225, 225, 225, 226, 227, 228, 228, 228, 228, - 228, 228, 228, 229, 228, 230, 231, 231, 231, 231, 231, 231, 18, 232, 165, 165, - 165, 165, 165, 233, 224, 26, 234, 9, 235, 236, 237, 238, 239, 240, 2, 2, - 2, 2, 2, 241, 242, 243, 2, 244, 2, 2, 2, 245, 14, 14, 246, 246, - 246, 246, 14, 247, 14, 14, 14, 246, 14, 14, 248, 14, 248, 14, 249, 250, - 14, 14, 251, 252, 0, 253, 0, 0, 254, 0, 255, 256, 0, 257, 2, 258, - 259, 26, 9, 9, 9, 9, 260, 26, 261, 262, 4, 0, 0, 263, 0, 0, - 2, 264, 0, 0, 0, 265, 26, 26, 0, 266, 26, 26, 267, 267, 267, 267, - 0, 0, 268, 0, 0, 0, 269, 0, 270, 270, 270, 270, 17, 17, 17, 17, - 17, 17, 271, 272, 166, 167, 273, 273, 273, 273, 273, 273, 273, 274, 275, 274, - 170, 170, 172, 26, 172, 172, 172, 172, 0, 0, 0, 276, 277, 277, 277, 278, - 277, 277, 277, 277, 277, 277, 279, 26, 277, 277, 280, 26, 26, 26, 0, 0, - 281, 0, 0, 0, 282, 283, 0, 284, 285, 286, 286, 286, 286, 286, 286, 286, - 286, 286, 287, 288, 289, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 291, - 292, 293, 293, 293, 293, 293, 294, 169, 169, 295, 0, 0, 293, 293, 293, 293, - 276, 296, 290, 290, 169, 169, 169, 295, 169, 169, 169, 297, 0, 0, 290, 290, - 290, 290, 290, 298, 290, 290, 290, 0, 299, 299, 299, 299, 299, 300, 299, 299, - 301, 26, 302, 302, 302, 302, 302, 302, 303, 303, 303, 303, 303, 304, 26, 26, - 305, 305, 305, 305, 305, 305, 305, 26, 306, 2, 2, 2, 2, 307, 2, 2, - 2, 308, 309, 258, 26, 26, 310, 2, 311, 311, 311, 311, 311, 312, 0, 265, - 313, 313, 313, 313, 313, 313, 313, 26, 314, 314, 314, 314, 315, 316, 314, 317, - 318, 318, 318, 318, 318, 319, 320, 320, 320, 320, 321, 322, 169, 169, 169, 323, - 324, 324, 324, 324, 324, 325, 324, 326, 164, 164, 164, 327, 328, 328, 328, 328, - 328, 328, 329, 26, 328, 330, 328, 331, 332, 332, 332, 332, 333, 26, 26, 334, - 335, 335, 336, 26, 337, 337, 337, 26, 172, 172, 2, 2, 2, 2, 2, 338, - 339, 340, 176, 176, 335, 335, 335, 335, 335, 341, 335, 342, 343, 26, 169, 169, - 295, 344, 169, 169, 169, 169, 169, 343, 277, 280, 277, 277, 277, 277, 277, 345, - 346, 26, 347, 348, 25, 25, 349, 350, 351, 25, 31, 31, 352, 26, 353, 31, - 31, 31, 31, 354, 31, 31, 355, 31, 31, 356, 26, 26, 26, 26, 31, 31, - 9, 9, 0, 265, 9, 357, 0, 0, 0, 0, 358, 0, 257, 359, 360, 31, - 31, 31, 31, 361, 362, 0, 0, 0, 363, 290, 289, 290, 290, 290, 290, 364, - 365, 365, 365, 366, 257, 257, 26, 367, 368, 369, 368, 368, 370, 368, 368, 371, - 368, 372, 368, 372, 368, 368, 368, 368, 368, 368, 368, 373, 374, 0, 0, 0, - 0, 0, 375, 0, 14, 252, 0, 376, 377, 26, 26, 26, 0, 0, 0, 378, - 379, 379, 379, 380, 381, 381, 381, 381, 381, 381, 382, 26, 383, 0, 0, 359, - 384, 384, 384, 384, 385, 386, 387, 387, 387, 388, 389, 389, 389, 389, 389, 390, - 391, 391, 391, 392, 393, 393, 393, 393, 394, 393, 395, 26, 396, 396, 396, 396, - 396, 396, 397, 397, 397, 397, 397, 397, 398, 398, 398, 399, 398, 400, 401, 401, - 401, 401, 402, 401, 401, 401, 401, 402, 403, 403, 403, 403, 403, 26, 404, 404, - 404, 404, 404, 404, 405, 406, 407, 408, 407, 408, 409, 407, 410, 407, 410, 411, - 412, 412, 412, 412, 412, 412, 413, 26, 414, 414, 414, 414, 414, 414, 415, 26, - 414, 414, 416, 26, 414, 26, 26, 26, 417, 2, 2, 2, 2, 2, 418, 419, - 420, 421, 422, 422, 422, 422, 423, 424, 425, 425, 426, 425, 427, 427, 427, 427, - 428, 428, 428, 429, 430, 428, 26, 26, 431, 431, 432, 433, 434, 434, 434, 435, - 436, 436, 436, 437, 438, 438, 438, 438, 439, 439, 439, 440, 439, 439, 441, 439, - 439, 439, 439, 439, 442, 443, 444, 445, 446, 446, 447, 448, 446, 449, 446, 449, - 450, 450, 450, 450, 451, 451, 451, 451, 452, 452, 452, 452, 453, 454, 453, 26, - 455, 455, 455, 455, 455, 455, 456, 457, 458, 458, 459, 458, 460, 460, 461, 460, - 462, 462, 463, 464, 26, 465, 26, 26, 466, 466, 466, 466, 466, 467, 26, 26, - 468, 468, 468, 468, 468, 468, 469, 26, 468, 468, 469, 470, 471, 471, 471, 471, - 471, 26, 471, 472, 473, 473, 473, 473, 474, 475, 473, 473, 474, 476, 26, 26, - 31, 31, 31, 50, 477, 477, 477, 477, 477, 478, 479, 26, 480, 26, 26, 26, - 26, 26, 26, 481, 482, 482, 482, 482, 482, 26, 483, 483, 483, 483, 483, 484, - 26, 26, 485, 485, 485, 486, 26, 26, 26, 26, 487, 487, 487, 488, 26, 26, - 489, 489, 490, 26, 491, 491, 491, 491, 491, 492, 493, 491, 491, 491, 492, 494, - 495, 495, 495, 495, 496, 497, 498, 498, 498, 499, 498, 500, 501, 501, 501, 501, - 501, 501, 502, 501, 501, 26, 503, 503, 503, 503, 504, 26, 505, 505, 505, 505, - 506, 137, 507, 26, 508, 508, 509, 508, 508, 508, 508, 508, 510, 26, 26, 26, - 511, 512, 513, 514, 513, 515, 516, 516, 516, 516, 516, 516, 516, 517, 516, 518, - 519, 520, 521, 522, 522, 523, 524, 525, 520, 526, 527, 528, 529, 530, 530, 26, - 531, 532, 531, 531, 531, 531, 533, 531, 534, 535, 533, 536, 537, 26, 26, 26, - 538, 538, 538, 538, 538, 538, 538, 539, 540, 26, 26, 26, 541, 541, 541, 541, - 541, 26, 541, 542, 543, 543, 543, 543, 543, 543, 544, 543, 543, 543, 543, 544, - 545, 545, 545, 545, 546, 26, 545, 547, 198, 548, 26, 26, 549, 549, 549, 549, - 549, 549, 549, 550, 549, 550, 164, 164, 551, 26, 26, 26, 552, 552, 552, 553, - 552, 554, 552, 552, 555, 26, 26, 26, 556, 556, 556, 556, 556, 556, 556, 557, - 558, 558, 558, 558, 558, 558, 559, 560, 561, 562, 563, 564, 564, 564, 565, 566, - 561, 26, 564, 567, 568, 569, 568, 568, 568, 568, 568, 569, 570, 26, 26, 26, - 571, 571, 571, 571, 571, 26, 572, 572, 572, 572, 572, 572, 573, 26, 178, 178, - 574, 574, 574, 574, 574, 574, 574, 575, 53, 576, 26, 26, 577, 577, 577, 577, - 578, 26, 577, 578, 579, 580, 579, 579, 579, 579, 581, 579, 582, 26, 579, 579, - 579, 583, 584, 584, 584, 584, 585, 584, 584, 586, 587, 26, 588, 589, 590, 590, - 590, 590, 588, 591, 590, 26, 590, 592, 593, 594, 595, 595, 595, 596, 597, 598, - 595, 599, 26, 26, 600, 600, 600, 601, 602, 602, 603, 602, 602, 602, 602, 604, - 602, 602, 602, 605, 26, 26, 606, 26, 108, 108, 108, 108, 108, 108, 607, 608, - 609, 609, 609, 609, 609, 609, 609, 610, 609, 611, 612, 26, 613, 26, 26, 26, - 26, 26, 614, 614, 614, 614, 614, 614, 614, 614, 615, 26, 616, 616, 616, 616, - 616, 616, 617, 26, 616, 616, 616, 618, 619, 619, 619, 619, 620, 26, 26, 26, - 621, 621, 621, 621, 621, 621, 621, 622, 305, 305, 305, 623, 624, 624, 624, 625, - 624, 626, 627, 627, 627, 627, 627, 627, 627, 627, 627, 628, 627, 629, 630, 630, - 630, 631, 631, 26, 632, 632, 632, 632, 633, 26, 632, 634, 634, 632, 632, 635, - 632, 632, 26, 26, 636, 636, 636, 636, 636, 636, 636, 637, 638, 638, 638, 638, - 638, 638, 638, 639, 640, 640, 640, 640, 640, 641, 640, 640, 640, 642, 640, 640, - 643, 26, 345, 26, 644, 644, 644, 644, 644, 644, 644, 26, 645, 645, 645, 645, - 645, 645, 646, 26, 26, 26, 26, 647, 644, 648, 26, 26, 26, 26, 649, 650, - 651, 286, 286, 286, 652, 26, 653, 26, 26, 26, 654, 26, 655, 26, 656, 656, - 656, 656, 656, 656, 656, 656, 656, 657, 658, 658, 658, 658, 658, 659, 658, 660, - 658, 661, 658, 662, 359, 26, 26, 26, 0, 0, 0, 265, 0, 0, 359, 26, - 9, 663, 9, 9, 221, 26, 0, 0, 0, 0, 276, 26, 257, 362, 0, 0, - 664, 665, 0, 666, 667, 668, 0, 0, 0, 669, 0, 0, 246, 26, 26, 26, - 0, 0, 257, 26, 0, 0, 0, 259, 0, 0, 254, 0, 0, 0, 0, 254, - 670, 671, 0, 672, 673, 0, 0, 0, 269, 674, 254, 254, 0, 0, 0, 675, - 676, 677, 678, 0, 276, 0, 0, 0, 0, 268, 0, 0, 679, 679, 679, 679, - 679, 680, 26, 681, 682, 679, 26, 26, 2, 2, 2, 346, 683, 419, 26, 26, - 684, 270, 270, 685, 686, 687, 18, 18, 18, 688, 26, 26, 26, 689, 26, 26, - 690, 690, 690, 690, 690, 691, 690, 692, 690, 693, 26, 26, 26, 26, 694, 694, - 694, 695, 26, 26, 696, 696, 696, 696, 696, 696, 696, 697, 26, 26, 698, 698, - 698, 698, 698, 699, 26, 26, 700, 700, 700, 700, 700, 701, 172, 702, 170, 172, - 703, 703, 703, 703, 704, 703, 705, 26, 706, 706, 706, 706, 706, 707, 706, 708, - 26, 26, 362, 0, 0, 0, 376, 26, 709, 31, 31, 31, 710, 711, 712, 713, - 714, 715, 710, 716, 710, 712, 712, 717, 31, 718, 31, 719, 720, 718, 31, 719, - 26, 26, 721, 26, 0, 359, 0, 0, 0, 257, 362, 0, 362, 0, 362, 0, - 0, 276, 26, 26, 722, 0, 0, 0, 723, 26, 0, 0, 0, 0, 0, 359, - 0, 259, 265, 26, 276, 26, 26, 26, 0, 0, 0, 724, 0, 376, 0, 376, - 0, 0, 257, 725, 0, 359, 259, 26, 0, 26, 0, 265, 0, 26, 0, 0, - 0, 276, 0, 359, 265, 26, 26, 26, 0, 276, 0, 376, 0, 726, 0, 0, - 257, 722, 0, 727, 0, 265, 0, 259, 277, 277, 277, 280, 345, 26, 277, 277, - 728, 26, 277, 277, 277, 729, 277, 277, 277, 277, 26, 26, 730, 26, 26, 26, - 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976, - 1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082, - 1086,1110, 0, 0,1124,1125,1126,1127,1131,1133, 0,1147,1154,1155,1156,1161, - 1187,1188,1189,1193, 0,1219,1226,1227,1228,1229,1233, 0, 0,1267,1268,1269, - 1273,1298, 0,1303, 943,1128, 944,1129, 954,1139, 958,1143, 959,1144, 960,1145, - 961,1146, 964,1149, 0, 0, 973,1158, 974,1159, 975,1160, 983,1168, 978,1163, - 988,1173, 990,1175, 991,1176, 993,1178, 994,1179, 0, 0,1004,1190,1005,1191, - 1006,1192,1014,1199,1007, 0, 0, 0,1016,1201,1020,1206, 0,1022,1208,1025, - 1211,1023,1209, 0, 0, 0, 0,1032,1218,1037,1223,1035,1221, 0, 0, 0, - 1044,1230,1045,1231,1049,1235, 0, 0,1058,1244,1064,1250,1060,1246,1066,1252, - 1067,1253,1072,1258,1069,1255,1077,1264,1074,1261, 0, 0,1083,1270,1084,1271, - 1085,1272,1088,1275,1089,1276,1096,1283,1103,1290,1111,1299,1115,1118,1307,1120, - 1309,1121,1310, 0,1053,1239, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,1093,1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 949,1134,1010,1195,1050,1236,1090,1277,1341,1368,1340,1367,1342,1369,1339, - 1366, 0,1320,1347,1418,1419,1323,1350, 0, 0, 992,1177,1018,1204,1055,1241, - 1416,1417,1415,1424,1202, 0, 0, 0, 987,1172, 0, 0,1031,1217,1321,1348, - 1322,1349,1338,1365, 950,1135, 951,1136, 979,1164, 980,1165,1011,1196,1012,1197, - 1051,1237,1052,1238,1061,1247,1062,1248,1091,1278,1092,1279,1071,1257,1076,1263, - 0, 0, 997,1182, 0, 0, 0, 0, 0, 0, 945,1130, 982,1167,1337,1364, - 1335,1362,1046,1232,1422,1423,1113,1301, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 8, 9, 0, 10,1425, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, 0,1314,1427, 5,1434,1438,1443, 0, - 1450, 0,1455,1461,1514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1446,1458, - 1468,1476,1480,1486,1517, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1489,1503, - 1494,1500,1508, 0, 0, 0, 0,1520,1521, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1526,1528, 0,1525, 0, 0, 0,1522, 0, 0, 0, 0, - 1536,1532,1539, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1534, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1556, 0, 0, - 0, 0, 0, 0,1548,1550, 0,1547, 0, 0, 0,1567, 0, 0, 0, 0, - 1558,1554,1561, 0, 0, 0, 0, 0, 0, 0,1568,1569, 0, 0, 0, 0, - 0, 0, 0, 0, 0,1529,1551, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1523,1545,1524,1546, 0, 0,1527,1549, 0, 0,1570,1571, - 1530,1552,1531,1553, 0, 0,1533,1555,1535,1557,1537,1559, 0, 0,1572,1573, - 1544,1566,1538,1560,1540,1562,1541,1563,1542,1564, 0, 0,1543,1565, 0, 0, - 0, 0, 0, 0, 0, 0,1606,1607,1609,1608,1610, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1613, 0,1611, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,1612, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1620, 0, 0, - 0, 0, 0, 0, 0,1623, 0, 0,1624, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1614,1615,1616,1617, - 1618,1619,1621,1622, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1628, - 1629, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1625,1626, 0,1627, 0, 0, 0,1634, 0, 0,1635, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1630,1631,1632, - 0, 0,1633, 0, 0, 0, 0, 0, 0, 0, 0, 0,1639, 0, 0,1638, - 1640, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1636,1637, 0, 0, 0, 0, 0, 0,1641, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1642,1644, - 1643, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1645, 0, 0, 0, - 0, 0, 0, 0,1646, 0, 0, 0, 0, 0, 0,1648,1649, 0,1647,1650, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1651,1653, - 1652, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1654, 0, - 1655,1657,1656, 0, 0, 0, 0,1659, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1660, 0, 0, 0, 0,1661, 0, 0, 0, 0,1662, 0, 0, 0, 0, - 1663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1658, 0, 0, - 0, 0, 0, 0, 0, 0, 0,1664, 0,1665,1673, 0,1674, 0, 0, 0, - 0, 0, 0, 0, 0,1666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,1668, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1669, 0, 0, 0, 0,1670, 0, 0, 0, 0,1671, 0, 0, 0, 0, - 1672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1667, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1675, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1676, 0,1677, 0,1678, 0, - 1679, 0,1680, 0, 0, 0,1681, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1682, - 0,1683, 0, 0,1684,1685, 0,1686, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 953,1138, 955,1140, 956,1141, 957,1142,1324,1351, 963,1148, - 965,1150, 968,1153, 966,1151, 967,1152,1378,1380,1379,1381, 984,1169, 985,1170, - 1420,1421, 986,1171, 989,1174, 995,1180, 998,1183, 996,1181, 999,1184,1000,1185, - 1015,1200,1329,1356,1017,1203,1019,1205,1021,1207,1024,1210,1687,1688,1027,1213, - 1026,1212,1028,1214,1029,1215,1030,1216,1034,1220,1036,1222,1039,1225,1038,1224, - 1334,1361,1336,1363,1382,1384,1383,1385,1056,1242,1057,1243,1059,1245,1063,1249, - 1689,1690,1065,1251,1068,1254,1070,1256,1386,1387,1388,1389,1691,1692,1073,1259, - 1075,1262,1079,1266,1078,1265,1095,1282,1098,1285,1097,1284,1390,1391,1392,1393, - 1099,1286,1100,1287,1101,1288,1102,1289,1105,1292,1104,1291,1106,1294,1107,1295, - 1108,1296,1114,1302,1119,1308,1122,1311,1123,1312,1186,1260,1293,1305, 0,1394, - 0, 0, 0, 0, 952,1137, 947,1132,1317,1344,1316,1343,1319,1346,1318,1345, - 1693,1695,1371,1375,1370,1374,1373,1377,1372,1376,1694,1696, 981,1166, 977,1162, - 972,1157,1326,1353,1325,1352,1328,1355,1327,1354,1697,1698,1009,1194,1013,1198, - 1054,1240,1048,1234,1331,1358,1330,1357,1333,1360,1332,1359,1699,1700,1396,1401, - 1395,1400,1398,1403,1397,1402,1399,1404,1094,1281,1087,1274,1406,1411,1405,1410, - 1408,1413,1407,1412,1409,1414,1109,1297,1117,1306,1116,1304,1112,1300, 0, 0, - 0, 0, 0, 0,1471,1472,1701,1705,1702,1706,1703,1707,1430,1431,1715,1719, - 1716,1720,1717,1721,1477,1478,1729,1731,1730,1732, 0, 0,1435,1436,1733,1735, - 1734,1736, 0, 0,1481,1482,1737,1741,1738,1742,1739,1743,1439,1440,1751,1755, - 1752,1756,1753,1757,1490,1491,1765,1768,1766,1769,1767,1770,1447,1448,1771,1774, - 1772,1775,1773,1776,1495,1496,1777,1779,1778,1780, 0, 0,1451,1452,1781,1783, - 1782,1784, 0, 0,1504,1505,1785,1788,1786,1789,1787,1790, 0,1459, 0,1791, - 0,1792, 0,1793,1509,1510,1794,1798,1795,1799,1796,1800,1462,1463,1808,1812, - 1809,1813,1810,1814,1467, 21,1475, 22,1479, 23,1485, 24,1493, 27,1499, 28, - 1507, 29, 0, 0,1704,1708,1709,1710,1711,1712,1713,1714,1718,1722,1723,1724, - 1725,1726,1727,1728,1740,1744,1745,1746,1747,1748,1749,1750,1754,1758,1759,1760, - 1761,1762,1763,1764,1797,1801,1802,1803,1804,1805,1806,1807,1811,1815,1816,1817, - 1818,1819,1820,1821,1470,1469,1822,1474,1465, 0,1473,1825,1429,1428,1426, 12, - 1432, 0, 26, 0, 0,1315,1823,1484,1466, 0,1483,1829,1433, 13,1437, 14, - 1441,1826,1827,1828,1488,1487,1513, 19, 0, 0,1492,1515,1445,1444,1442, 15, - 0,1831,1832,1833,1502,1501,1516, 25,1497,1498,1506,1518,1457,1456,1454, 17, - 1453,1313, 11, 3, 0, 0,1824,1512,1519, 0,1511,1830,1449, 16,1460, 18, - 1464, 4, 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 6, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1834,1835, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1836, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1837,1839,1838, 0, 0, 0, 0,1840, 0, 0, 0, 0,1841, 0, 0, - 1842, 0, 0, 0, 0, 0, 0, 0,1843, 0,1844, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,1845, 0, 0,1846, 0, 0,1847, 0,1848, 0, 0, - 0, 0, 0, 0, 937, 0,1850, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1849, 936, 938,1851,1852, 0, 0,1853,1854, 0, 0,1855,1856, 0, 0, - 0, 0, 0, 0,1857,1858, 0, 0,1861,1862, 0, 0,1863,1864, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1867,1868,1869,1870,1859,1860,1865,1866, 0, 0, 0, 0, 0, 0,1871,1872, - 1873,1874, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1875, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1877, 0,1878, 0,1879, 0,1880, 0,1881, 0,1882, 0,1883, 0,1884, 0, - 1885, 0,1886, 0,1887, 0,1888, 0, 0,1889, 0,1890, 0,1891, 0, 0, - 0, 0, 0, 0,1892,1893, 0,1894,1895, 0,1896,1897, 0,1898,1899, 0, - 1900,1901, 0, 0, 0, 0, 0, 0,1876, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1902, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1904, 0,1905, 0,1906, 0,1907, 0,1908, 0,1909, 0,1910, 0,1911, 0, - 1912, 0,1913, 0,1914, 0,1915, 0, 0,1916, 0,1917, 0,1918, 0, 0, - 0, 0, 0, 0,1919,1920, 0,1921,1922, 0,1923,1924, 0,1925,1926, 0, - 1927,1928, 0, 0, 0, 0, 0, 0,1903, 0, 0,1929,1930,1931,1932, 0, - 0, 0,1933, 0, 710, 385, 724, 715, 455, 103, 186, 825, 825, 242, 751, 205, - 241, 336, 524, 601, 663, 676, 688, 738, 411, 434, 474, 500, 649, 746, 799, 108, - 180, 416, 482, 662, 810, 275, 462, 658, 692, 344, 618, 679, 293, 388, 440, 492, - 740, 116, 146, 168, 368, 414, 481, 527, 606, 660, 665, 722, 781, 803, 809, 538, - 553, 588, 642, 758, 811, 701, 233, 299, 573, 612, 487, 540, 714, 779, 232, 267, - 412, 445, 457, 585, 594, 766, 167, 613, 149, 148, 560, 589, 648, 768, 708, 345, - 411, 704, 105, 259, 313, 496, 518, 174, 542, 120, 307, 101, 430, 372, 584, 183, - 228, 529, 650, 697, 424, 732, 428, 349, 632, 355, 517, 110, 135, 147, 403, 580, - 624, 700, 750, 170, 193, 245, 297, 374, 463, 543, 763, 801, 812, 815, 162, 384, - 420, 730, 287, 330, 337, 366, 459, 476, 509, 558, 591, 610, 726, 652, 734, 759, - 154, 163, 198, 473, 683, 697, 292, 311, 353, 423, 572, 494, 113, 217, 259, 280, - 314, 499, 506, 603, 608, 752, 778, 782, 788, 117, 557, 748, 774, 320, 109, 126, - 260, 265, 373, 411, 479, 523, 655, 737, 823, 380, 765, 161, 395, 398, 438, 451, - 502, 516, 537, 583, 791, 136, 340, 769, 122, 273, 446, 727, 305, 322, 400, 496, - 771, 155, 190, 269, 377, 391, 406, 432, 501, 519, 599, 684, 687, 749, 776, 175, - 452, 191, 480, 510, 659, 772, 805, 813, 397, 444, 619, 566, 568, 575, 491, 471, - 707, 111, 636, 156, 153, 288, 346, 578, 256, 435, 383, 729, 680, 767, 694, 295, - 128, 210, 0, 0, 227, 0, 379, 0, 0, 150, 493, 525, 544, 551, 552, 556, - 783, 576, 604, 0, 661, 0, 703, 0, 0, 735, 743, 0, 0, 0, 793, 794, - 795, 808, 741, 773, 118, 127, 130, 166, 169, 177, 207, 213, 215, 226, 229, 268, - 270, 317, 327, 329, 335, 369, 375, 381, 404, 441, 448, 458, 477, 484, 503, 539, - 545, 547, 546, 548, 549, 550, 554, 555, 561, 564, 569, 591, 593, 595, 598, 607, - 620, 625, 625, 651, 690, 695, 705, 706, 716, 717, 733, 735, 777, 786, 790, 315, - 869, 623, 0, 0, 102, 145, 134, 115, 129, 138, 165, 171, 207, 202, 206, 212, - 227, 231, 240, 243, 250, 254, 294, 296, 303, 308, 319, 325, 321, 329, 326, 335, - 341, 357, 360, 362, 370, 379, 388, 389, 393, 421, 424, 438, 456, 454, 458, 465, - 477, 535, 485, 490, 493, 507, 512, 514, 521, 522, 525, 526, 528, 533, 532, 541, - 565, 569, 574, 586, 591, 597, 607, 637, 647, 674, 691, 693, 695, 698, 703, 699, - 705, 704, 702, 706, 709, 717, 728, 736, 747, 754, 770, 777, 783, 784, 786, 787, - 790, 802, 825, 848, 847, 857, 55, 65, 66, 883, 892, 916, 822, 824, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1586, 0,1605, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1602,1603, - 1934,1935,1574,1575,1576,1577,1579,1580,1581,1583,1584, 0,1585,1587,1588,1589, - 1591, 0,1592, 0,1593,1594, 0,1595,1596, 0,1598,1599,1600,1601,1604,1582, - 1578,1590,1597, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1936, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,1937, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1938, 0, - 1939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1940, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1941,1942, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1943, - 1944, 0, 0, 0, 0, 0, 0,1945, 0,1946, 0, 0, 0, 0, 0, 0, - 0, 0,1947, 0, 0,1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,1950, 0,1949,1951, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1953, - 1952, 0,1954, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1955,1956, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1957, 0, 0, 0, - 0, 0, 0, 0, 0,1958,1961,1959,1965,1960,1962,1964,1963, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1967,1966,1968, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1969,1970,1971,1972,1973,1974,1975, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1976, - 1977,1978,1980,1979,1981, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 106, 104, 107, 826, 114, 118, 119, 121, 123, 124, 127, 125, - 34, 830, 130, 131, 132, 137, 827, 35, 133, 139, 829, 142, 143, 112, 144, 145, - 924, 151, 152, 37, 157, 158, 159, 160, 38, 165, 166, 169, 171, 172, 173, 174, - 176, 177, 178, 179, 181, 182, 182, 182, 833, 468, 184, 185, 834, 187, 188, 189, - 196, 192, 194, 195, 197, 199, 200, 201, 203, 204, 204, 206, 208, 209, 211, 218, - 213, 219, 214, 216, 153, 234, 221, 222, 223, 220, 225, 224, 230, 835, 235, 236, - 237, 238, 239, 244, 836, 837, 247, 248, 249, 246, 251, 39, 40, 253, 255, 255, - 838, 257, 258, 259, 261, 839, 262, 263, 301, 264, 41, 266, 270, 272, 271, 841, - 274, 842, 277, 276, 278, 281, 282, 42, 283, 284, 285, 286, 43, 843, 44, 289, - 290, 291, 293, 934, 298, 845, 845, 621, 300, 300, 45, 852, 894, 302, 304, 46, - 306, 309, 310, 312, 316, 48, 47, 317, 846, 318, 323, 324, 325, 324, 328, 329, - 333, 331, 332, 334, 335, 336, 338, 339, 342, 343, 347, 351, 849, 350, 348, 352, - 354, 359, 850, 361, 358, 356, 49, 363, 365, 367, 364, 50, 369, 371, 851, 376, - 386, 378, 53, 381, 52, 51, 140, 141, 387, 382, 614, 78, 388, 389, 390, 394, - 392, 856, 54, 399, 396, 402, 404, 858, 405, 401, 407, 55, 408, 409, 410, 413, - 859, 415, 56, 417, 860, 418, 57, 419, 422, 424, 425, 861, 840, 862, 426, 863, - 429, 431, 427, 433, 437, 441, 438, 439, 442, 443, 864, 436, 449, 450, 58, 454, - 453, 865, 447, 460, 866, 867, 461, 466, 465, 464, 59, 467, 470, 469, 472, 828, - 475, 868, 478, 870, 483, 485, 486, 871, 488, 489, 872, 873, 495, 497, 60, 498, - 61, 61, 504, 505, 507, 508, 511, 62, 513, 874, 515, 875, 518, 844, 520, 876, - 877, 878, 63, 64, 528, 880, 879, 881, 882, 530, 531, 531, 533, 66, 534, 67, - 68, 884, 536, 538, 541, 69, 885, 549, 886, 887, 556, 559, 70, 561, 562, 563, - 888, 889, 889, 567, 71, 890, 570, 571, 72, 891, 577, 73, 581, 579, 582, 893, - 587, 74, 590, 592, 596, 75, 895, 896, 76, 897, 600, 898, 602, 605, 607, 899, - 900, 609, 901, 611, 853, 77, 615, 616, 79, 617, 252, 902, 903, 854, 855, 621, - 622, 731, 80, 627, 626, 628, 164, 629, 630, 631, 633, 904, 632, 634, 639, 640, - 635, 641, 646, 651, 638, 643, 644, 645, 905, 907, 906, 81, 653, 654, 656, 911, - 657, 908, 82, 83, 909, 910, 84, 664, 665, 666, 667, 669, 668, 671, 670, 674, - 672, 673, 675, 85, 677, 678, 86, 681, 682, 912, 685, 686, 87, 689, 36, 913, - 914, 88, 89, 696, 702, 709, 711, 915, 712, 713, 718, 719, 917, 831, 721, 720, - 723, 832, 725, 728, 918, 919, 739, 742, 744, 920, 745, 753, 756, 757, 755, 760, - 761, 921, 762, 90, 764, 922, 91, 775, 279, 780, 923, 925, 92, 93, 785, 926, - 94, 927, 787, 787, 789, 928, 792, 95, 796, 797, 798, 800, 96, 929, 802, 804, - 806, 97, 98, 807, 930, 99, 931, 932, 933, 814, 100, 816, 817, 818, 819, 820, - 821, 935, 0, 0, -}; -static const int16_t -_hb_ucd_i16[92] = -{ - 0, 0, 1, -1, 2, 0, -2, 0, 0, 2, 0, -2, 0, 16, 0, -16, - 0, 1, -1, 0, 3, 3, 3, -3, -3, -3, 0, 2016, 0, 2527, 1923, 1914, - 1918, 0, 2250, 0, 0, 138, 0, 7, -7, 0, -1, 1, 1824, 0, 2104, 0, - 2108, 2106, 0, 2106, 1316, 0, -1, -138, 8, 8, 8, 0, 7, 7, -8, -8, - -8, -7,-1316, 1, -1, 3, -3, 1, 0,-1914,-1918, 0, 0,-1923,-1824, 0, - 0,-2016,-2104, 0, 0,-2106,-2108,-2106,-2250, 0,-2527, 0, -}; - -static inline uint_fast8_t -_hb_ucd_gc (unsigned u) -{ - return u<1114110u?_hb_ucd_u8[6472+(((_hb_ucd_u8[816+(((_hb_ucd_u16[((_hb_ucd_u8[272+(((_hb_ucd_u8[u>>1>>3>>4>>4])<<4)+((u>>1>>3>>4)&15u))])<<4)+((u>>1>>3)&15u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; -} -static inline uint_fast8_t -_hb_ucd_ccc (unsigned u) -{ - return u<125259u?_hb_ucd_u8[8504+(((_hb_ucd_u8[7936+(((_hb_ucd_u8[7460+(((_hb_ucd_u8[7100+(((_hb_ucd_u8[6854+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; -} -static inline unsigned -_hb_ucd_b4 (const uint8_t* a, unsigned i) -{ - return (a[i>>1]>>((i&1u)<<2))&15u; -} -static inline int_fast16_t -_hb_ucd_bmg (unsigned u) -{ - return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9396+(((_hb_ucd_u8[9164+(((_hb_ucd_u8[9068+(((_hb_ucd_b4(9004+_hb_ucd_u8,u>>1>>2>>3>>3))<<3)+((u>>1>>2>>3)&7u))])<<3)+((u>>1>>2)&7u))])<<2)+((u>>1)&3u))])<<1)+((u)&1u)]:0; -} -static inline uint_fast8_t -_hb_ucd_sc (unsigned u) -{ - return u<918000u?_hb_ucd_u8[10398+(((_hb_ucd_u16[3952+(((_hb_ucd_u16[2624+(((_hb_ucd_u8[9870+(((_hb_ucd_u8[9644+(u>>3>>2>>3>>4)])<<4)+((u>>3>>2>>3)&15u))])<<3)+((u>>3>>2)&7u))])<<2)+((u>>3)&3u))])<<3)+((u)&7u))]:2; -} -static inline uint_fast16_t -_hb_ucd_dm (unsigned u) -{ - return u<195102u?_hb_ucd_u16[6244+(((_hb_ucd_u8[16628+(((_hb_ucd_u8[16246+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; -} - - -#else - -static const uint8_t -_hb_ucd_u8[13730] = -{ - 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 7, 11, 12, 12, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 7, 21, 22, 22, 22, 23, 24, 7, 7, - 7, 25, 22, 22, 22, 26, 27, 28, 22, 29, 30, 31, 32, 33, 34, 35, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 21, 22, 36, - 7, 7, 7, 7, 37, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 38, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 34, 34, 34, 35, 36, 37, 34, 34, 34, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 67, 68, 69, 67, 70, 71, - 67, 67, 62, 72, 62, 62, 73, 67, 74, 75, 76, 77, 78, 67, 67, 67, - 79, 80, 34, 81, 82, 83, 67, 67, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 84, 34, 34, 34, 34, - 85, 34, 34, 34, 34, 34, 34, 34, 34, 86, 34, 34, 87, 88, 89, 90, - 91, 92, 93, 94, 95, 96, 97, 98, 34, 34, 34, 34, 34, 34, 34, 34, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, - 100,100, 34, 34, 34, 34,101,102, 34, 34,103,104,105,106,107,108, - 34, 34,109,110,111,112,113,114,115,116,117,118, 34, 34, 34,119, - 120,121,122,123,124,125,126,127, 34,128,129,130,131,132,133,134, - 135,136,137,138,139,140,141,142,143,144,111,145,146,147,148,111, - 149,150,151,152,153,154,155,156,157,158,159,160,111,161,162,163, - 34, 34, 34, 34, 34, 34, 34, 34,164, 34, 34,111,111,111,111,111, - 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,165, - 34, 34, 34, 34, 34, 34, 34, 34,166, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111, - 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, - 111,111,167,111,111,111,111,111,111,111,111,111,111,111,111,111, - 34, 34, 34, 34,168,169,170, 34,111,111,171,111,172,173,174,175, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111, - 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,119, - 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111, - 111,111,111,111,111,111,111,111, 34,176,111,111,111,111,111,111, - 111,111,111,111,111,111,111,111, 67,177, 67, 67, 67, 67,178, 67, - 67, 67,179,180,181,131, 65,111,182,183,184,185,186,187,188,189, - 67, 67, 67, 67,190,191,111,111,111,111,111,111,111,111,192,111, - 193,194,195,111,111,196,111,111,111,197,111,198,111,111,111, 34, - 34,199,200,111,111,111,111,111,131,201,202,111, 34,203,111,111, - 67, 67,204, 67, 67,111, 67,205, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67,177,111,111,111,111,111,111,111,111, - 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111,111, - 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111, - 206,111,194,194,111,111,111,111,111,111,111,111,111,111,111,111, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, - 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, - 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, - 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, - 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, - 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, - 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, - 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, - 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, - 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, - 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, - 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 16, 44, 16, 10, - 41, 41, 41, 45, 11, 11, 11, 11, 34, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, - 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 46, 34, 32, 34, 11, - 32, 47, 43, 43, 48, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, - 11, 11, 11, 11, 49, 2, 2, 2, 16, 16, 16, 16, 50, 51, 52, 53, - 54, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 55, - 56, 57, 43, 56, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 36, 36, - 36, 58, 2, 2, 2, 2, 2, 2, 59, 59, 59, 8, 9, 60, 2, 61, - 43, 43, 43, 43, 43, 57, 62, 2, 63, 36, 36, 36, 36, 64, 43, 43, - 7, 7, 7, 7, 7, 2, 2, 36, 65, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 66, 43, 43, 43, 67, 47, 43, 43, 68, 69, 70, 43, 43, 36, - 7, 7, 7, 7, 7, 36, 71, 72, 2, 2, 2, 2, 2, 2, 2, 73, - 64, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 65, 36, - 36, 36, 36, 43, 43, 43, 43, 43, 7, 7, 7, 7, 7, 36, 36, 36, - 36, 36, 36, 36, 36, 64, 43, 43, 43, 43, 40, 21, 2, 40, 69, 20, - 36, 36, 36, 43, 43, 69, 43, 43, 43, 43, 69, 43, 69, 43, 43, 43, - 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, 64, 43, 43, 2, - 36, 36, 36, 36, 74, 36, 36, 36, 59, 59, 59, 75, 43, 43, 43, 43, - 36, 36, 36, 36, 76, 43, 43, 43, 43, 75, 43, 43, 43, 43, 43, 43, - 43, 77, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 65, 78, - 79, 43, 43, 43, 77, 78, 79, 78, 64, 43, 43, 43, 36, 36, 36, 36, - 36, 43, 2, 7, 7, 7, 7, 7, 80, 36, 36, 36, 36, 36, 36, 36, - 64, 78, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 65, 78, - 79, 43, 43, 77, 78, 78, 79, 36, 36, 36, 36, 82, 78, 78, 36, 36, - 36, 43, 43, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 53, 58, 43, - 43, 77, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 78, - 79, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 65, 36, 36, 36, - 36, 36, 36, 7, 7, 7, 7, 7, 43, 36, 64, 2, 2, 2, 2, 2, - 79, 43, 43, 43, 77, 78, 79, 43, 60, 20, 20, 20, 83, 43, 43, 43, - 43, 78, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 65, 79, - 79, 43, 43, 77, 78, 78, 79, 43, 43, 43, 43, 77, 78, 78, 36, 36, - 72, 27, 27, 27, 27, 27, 27, 27, 43, 65, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 78, 77, 78, 78, 78, 78, 78, 79, 43, - 36, 36, 36, 82, 78, 78, 78, 78, 78, 78, 78, 7, 7, 7, 7, 7, - 27, 84, 61, 61, 53, 61, 61, 61, 77, 78, 65, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 65, 43, 77, 78, 78, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 36, 36, 36, 36, 7, 7, 7, 85, 27, 27, 27, 84, - 64, 78, 66, 36, 36, 36, 36, 36, 78, 78, 78, 77, 78, 78, 43, 43, - 43, 43, 77, 78, 78, 78, 81, 36, 86, 82, 78, 78, 78, 78, 78, 78, - 43, 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 65, 78, - 79, 43, 43, 78, 78, 78, 79, 71, 61, 61, 36, 82, 27, 27, 27, 87, - 27, 27, 27, 27, 84, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 77, - 78, 43, 43, 43, 78, 78, 78, 78, 7, 78, 2, 2, 2, 2, 2, 2, - 64, 36, 43, 43, 43, 43, 43, 88, 36, 36, 36, 69, 43, 43, 43, 57, - 7, 7, 7, 7, 7, 2, 2, 2, 64, 36, 43, 43, 43, 43, 65, 36, - 36, 36, 36, 40, 43, 43, 43, 43, 7, 7, 7, 7, 7, 7, 36, 36, - 71, 61, 2, 2, 2, 2, 2, 2, 2, 89, 89, 61, 43, 61, 61, 61, - 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 47, 47, 47, 4, 4, 78, - 64, 43, 43, 43, 43, 43, 43, 77, 43, 43, 57, 43, 36, 36, 64, 43, - 43, 43, 43, 43, 43, 43, 43, 61, 61, 61, 61, 70, 61, 61, 61, 61, - 2, 2, 89, 61, 21, 2, 2, 2, 36, 36, 36, 36, 36, 82, 79, 43, - 77, 43, 43, 43, 79, 77, 79, 65, 36, 36, 36, 78, 43, 36, 36, 43, - 65, 78, 81, 82, 78, 78, 78, 36, 64, 43, 65, 36, 36, 36, 36, 36, - 36, 77, 79, 77, 78, 78, 79, 82, 7, 7, 7, 7, 7, 78, 79, 61, - 16, 16, 16, 16, 16, 50, 44, 16, 36, 36, 36, 36, 36, 36, 64, 43, - 2, 2, 2, 2, 90, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, - 61, 61, 61, 61, 61, 61, 61, 61, 11, 11, 11, 11, 16, 16, 16, 16, - 91, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 71, 66, - 92, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 93, 94, 94, - 36, 36, 36, 36, 36, 58, 2, 95, 96, 36, 36, 36, 36, 36, 36, 36, - 36, 43, 77, 78, 78, 78, 78, 81, 36, 43, 97, 2, 2, 2, 2, 2, - 36, 43, 43, 43, 43, 43, 43, 43, 36, 36, 43, 79, 43, 43, 43, 78, - 78, 78, 78, 77, 79, 43, 43, 43, 43, 43, 2, 80, 2, 60, 64, 43, - 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 98, 2, 56, 43, 75, - 36, 76, 36, 36, 36, 36, 36, 36, 36, 36, 64, 65, 36, 36, 36, 36, - 36, 36, 36, 36, 64, 36, 36, 36, 43, 77, 78, 79, 77, 78, 78, 78, - 78, 77, 78, 78, 79, 43, 43, 43, 61, 61, 2, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 27, 27, 61, 36, 36, 36, 64, 77, 79, 43, 2, - 36, 36, 82, 77, 43, 43, 43, 43, 77, 77, 79, 43, 43, 43, 77, 78, - 78, 79, 43, 43, 43, 43, 43, 43, 2, 2, 2, 80, 2, 2, 2, 2, - 43, 43, 43, 43, 43, 43, 43, 99, 43, 43, 81, 36, 36, 36, 36, 36, - 36, 36, 77, 43, 43, 77, 77, 78, 78, 77, 81, 36, 36, 36, 36, 2, - 89, 61, 61, 61, 61, 47, 43, 43, 43, 43, 61, 61, 61, 61, 21, 2, - 43, 81, 36, 36, 36, 36, 36, 36, 82, 43, 43, 78, 43, 79, 43, 36, - 36, 36, 36, 77, 43, 78, 79, 79, 43, 78, 78, 78, 78, 78, 2, 2, - 36, 36, 78, 78, 78, 78, 43, 43, 43, 43, 78, 43, 43, 57, 2, 2, - 7, 7, 7, 7, 7, 7, 86, 36, 36, 36, 36, 36, 40, 40, 40, 2, - 16, 16, 16, 16, 34, 16, 16, 16, 43, 57, 43, 43, 43, 43, 43, 43, - 77, 43, 43, 43, 65, 36, 64, 36, 36, 36, 65, 82, 43, 36, 36, 36, - 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 44, 16, 16, - 16, 16, 16, 16, 44, 16, 16, 16, 16, 16, 16, 16, 16,100, 40, 40, - 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, - 16, 16, 16, 16, 34, 11, 11, 11, 16, 16, 16, 16,101,101,101,101, - 16, 16, 16, 16, 11, 11,102,103, 41, 16, 16, 16, 11, 11,102, 41, - 16, 16, 16, 16, 11, 11,104, 41,105,105,105,105,105,106, 59, 59, - 51, 51, 51, 2,107,108,107,108, 2, 2, 2, 2,109, 59, 59,110, - 2, 2, 2, 2,111,112, 2,113,114, 2,115,116, 2, 2, 2, 2, - 2, 9,114, 2, 2, 2, 2,117, 59, 59, 59, 59, 59, 59, 59, 59, - 118, 40, 27, 27, 27, 8,115,119, 27, 27, 27, 27, 27, 8,115, 94, - 20, 20, 20, 20, 20, 20, 20, 20, 43, 43, 43, 43, 43, 43,120, 48, - 99, 48, 99, 43, 43, 43, 43, 43, 61,121, 61,122, 61, 34, 11, 16, - 11, 32,122, 61, 46, 11, 11, 61, 61, 61,121,121,121, 11, 11,123, - 11, 11, 35, 36, 39, 61, 16, 11, 8, 8, 46, 16, 16, 26, 61,124, - 95, 95, 95, 95, 95, 95, 95, 95, 95,125,126, 95,127, 61, 61, 61, - 8, 8,128, 61, 61, 8, 61, 61,128, 26, 61,128, 61, 61, 61,128, - 61, 61, 61, 61, 61, 61, 61, 8, 61,128,128, 61, 61, 61, 61, 61, - 61, 61, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 61, 61, 61, 61, 4, 4, 61, 61, 8, 61, 61, 61,129,130, 61, 61, - 61, 61, 61, 61, 61, 61,128, 61, 61, 61, 61, 61, 61, 26, 8, 8, - 8, 8, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 8, 8, - 8, 61, 61, 61, 61, 61, 61, 61, 27, 27, 27, 27, 27, 27, 61, 61, - 61, 61, 61, 61, 61, 27, 27, 27, 61, 61, 61, 26, 61, 61, 61, 61, - 26, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 8, 8, 8, 8, - 61, 61, 61, 61, 61, 61, 61, 26, 61, 61, 61, 61, 4, 4, 4, 4, - 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 61, 61, 61, 61, 61, 61, - 8, 8,115,131, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, - 8,115,132,132,132,132,132,132,132,132,132,132,131, 8, 8, 8, - 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, - 8, 8,128, 26, 8, 8,128, 61, 32, 11, 32, 34, 34, 34, 34, 11, - 32, 32, 34, 16, 16, 16, 40, 11, 32, 32,124, 61, 61,122, 34,133, - 43, 32, 16, 16, 50, 2, 90, 2, 36, 36, 36, 36, 36, 36, 36, 76, - 2, 2, 2, 2, 2, 2, 2, 56, 2,107,107, 2,111,112,107, 2, - 2, 2, 2, 6, 2, 98,107, 2,107, 4, 4, 4, 4, 2, 2, 80, - 2, 2, 2, 2, 2, 51, 2, 2, 98,134, 2, 2, 2, 2, 2, 2, - 61, 2,135,132,132,132,136, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 1, 2,137,138, 4, 4, 4, 4, 4, 61, 4, 4, 4, 4,139, 94, - 140, 95, 95, 95, 95, 43, 43, 78,141, 40, 40, 61, 95,142, 58, 61, - 72, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64,143,144, 63, - 36, 36, 36, 36, 36, 58, 40, 63, 61, 27, 27, 61, 61, 61, 61, 61, - 27, 27, 27, 27, 27, 61, 61, 61, 61, 61, 61, 61, 27, 27, 27, 27, - 145, 27, 27, 27, 27, 27, 27, 27, 36, 36, 76, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36,146, 2, 32, 32, 32, 32, 32, 32, 32, 64, - 48,147, 43, 43, 43, 43, 43, 80, 32, 32, 32, 32, 32, 32, 40, 43, - 36, 36, 36, 95, 95, 95, 95, 95, 43, 2, 2, 2, 2, 2, 2, 2, - 41, 41, 41,144, 40, 40, 40, 40, 41, 32, 32, 32, 32, 32, 32, 32, - 16, 32, 32, 32, 32, 32, 32, 32, 44, 16, 16, 16, 34, 34, 34, 32, - 32, 32, 32, 32, 42,148, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, - 32, 32, 11, 11, 34, 34, 32, 16, 32, 16, 16, 32, 32, 32, 11, 11, - 11, 40,149, 35, 40, 35, 36, 36, 36, 65, 36, 65, 36, 64, 36, 36, - 36, 82, 79, 77, 61, 61, 43, 43, 27, 27, 27, 61,150, 61, 61, 61, - 36, 36, 2, 2, 2, 2, 2, 2, 78, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 78, 78, 78, 78, 78, 78, 78, 78, 43, 43, 43, 43, 43, 2, - 43, 36, 36, 36, 2, 66, 66, 64, 36, 36, 36, 43, 43, 43, 43, 2, - 36, 36, 36, 64, 43, 43, 43, 43, 43, 78, 78, 78, 78, 78, 78, 97, - 36, 64, 78, 43, 43, 78, 43, 78, 97, 2, 2, 2, 2, 2, 2, 80, - 7, 7, 7, 7, 7, 7, 7, 2, 36, 36, 64, 63, 36, 36, 36, 36, - 36, 36, 36, 36, 64, 43, 43, 77, 79, 77, 79, 43, 43, 43, 43, 43, - 36, 64, 36, 36, 36, 36, 77, 78, 7, 7, 7, 7, 7, 7, 2, 2, - 63, 36, 36, 71, 61, 82, 77, 36, 65, 43, 65, 64, 65, 36, 36, 43, - 36, 36, 36, 36, 36, 36, 76, 2, 36, 36, 36, 36, 36, 82, 43, 78, - 2, 76,151, 43, 43, 43, 43, 43, 16, 16, 16, 16, 16,103, 40, 40, - 16, 16, 16, 16,100, 41, 41, 41, 36, 82, 79, 78, 77, 97, 79, 43, - 152,152,152,152,152,152,152,152,153,153,153,153,153,153,153,153, - 16, 16, 16, 16, 16, 16, 35, 65, 36, 36, 36, 36,154, 36, 36, 36, - 36, 41, 41, 41, 41, 41, 41, 41, 41, 74, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36,132, 36, 36, 36, 36, 36, 36, 36, 71, - 36, 36, 36, 36, 36, 36,150, 61, 2, 2, 2,135,116, 2, 2, 2, - 6,155,156,132,132,132,132,132,132,132,116,135,116, 2,113,157, - 2, 2, 2, 2,139,132,132,116, 2,158, 8, 8, 60, 2, 2, 2, - 36, 36, 36, 36, 36, 36, 36,159, 2, 2, 3, 2, 4, 5, 6, 2, - 16, 16, 16, 16, 16, 17, 18,115,116, 4, 2, 36, 36, 36, 36, 36, - 63, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, - 20,160, 53, 20, 26, 8,128, 61, 61, 61, 61, 61,161, 59, 61, 61, - 2, 2, 2, 90, 27, 27, 27, 27, 27, 27, 27, 84, 61, 61, 61, 61, - 95, 95,127, 27, 84, 61, 61, 61, 61, 61, 61, 61, 61, 27, 61, 61, - 61, 61, 61, 61, 61, 61, 47, 43,162,162,162,162,162,162,162,162, - 163, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 87, 36, - 138, 36, 36, 36, 36, 95, 95, 95, 36, 36, 36, 36, 36, 36, 36, 58, - 164, 95, 95, 95, 95, 95, 95, 95, 11, 11, 11, 32, 16, 16, 16, 16, - 36, 36, 36, 58, 27, 27, 27, 27, 36, 36, 36, 71,145, 27, 27, 27, - 36, 36, 36,165, 27, 27, 27, 27, 36, 36, 36, 36, 36,165, 27, 27, - 36, 36, 36, 27, 27, 27, 27, 30, 36, 36, 36, 36, 36, 36, 27, 36, - 64, 43, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36,165, 30, 36, 36, 36, 36, 36, 36,165, 27, - 36, 36, 36, 36, 72, 36, 36, 36, 36, 36, 64, 43, 43,163, 27, 27, - 36, 36, 36, 36, 58, 2, 2, 2, 36, 36, 36, 36, 27, 27, 27, 27, - 16, 16, 16, 16, 16, 27, 27, 27, 36, 36, 43, 43, 43, 43, 43, 43, - 7, 7, 7, 7, 7, 36, 36, 63, 11, 11, 11, 11,166, 43, 43,141, - 16, 16, 16, 16, 16, 16, 16, 8, 36, 36, 36, 36, 36, 64,167, 51, - 36, 36, 36, 36, 36, 36, 43, 43, 27, 27, 27, 87, 36, 36, 36, 36, - 163, 27, 30, 2, 2, 2, 2, 2, 36, 43, 43, 2, 2, 2, 2, 2, - 36, 36,165, 27, 27, 27, 27, 27, 79, 81, 36, 36, 36, 36, 36, 36, - 43, 43, 43, 57, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 7, 7, 7, 7, 7, 65, 64, 65, 36, 36, 36, 36, 64, - 78, 79, 43, 77, 79, 57, 73, 2, 2, 43, 43, 43, 43, 43, 67, 59, - 36, 36, 36, 64, 43, 43, 79, 43, 43, 43, 43, 7, 7, 7, 7, 7, - 2, 2, 82, 81, 36, 36, 36, 36, 36, 64, 2, 36, 36, 36, 36, 36, - 36, 82, 78, 43, 43, 43, 43, 77, 81, 36, 58, 2, 56, 43, 57, 79, - 7, 7, 7, 7, 7, 58, 58, 2, 90, 27, 27, 27, 27, 27, 27, 27, - 36, 36, 36, 36, 36, 36, 78, 79, 43, 78, 77, 43, 2, 2, 2, 65, - 36, 36, 36, 36, 36, 36, 36, 64, 77, 78, 78, 78, 78, 78, 78, 78, - 36, 36, 36, 82, 78, 78, 81, 36, 36, 78, 78, 43, 43, 43, 43, 43, - 36, 36, 36, 36, 78, 79, 43, 43, 43, 78, 78, 78, 78, 78, 78, 77, - 65, 65, 2, 2, 2, 2, 2, 2, 56, 43, 43, 43, 43, 43, 43, 43, - 36, 36, 82, 78, 43, 43, 43, 43, 78, 43, 77, 65, 36, 58, 2, 2, - 7, 7, 7, 7, 7, 2, 2, 65, 78, 79, 43, 43, 77, 77, 78, 79, - 77, 43, 36, 66, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 82, - 78, 43, 43, 43, 78, 78, 43, 79, 57, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 36, 36, 43, 43, 78, 79, 43, 43, 43, 77, 79, 79, - 57, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 79, 78, - 43, 43, 43, 79, 58, 2, 2, 2, 36, 36, 36, 36, 36, 36, 64, 79, - 78, 43, 43, 79, 43, 43, 43, 43, 7, 7, 7, 7, 7, 27, 2, 89, - 43, 43, 43, 43, 79, 57, 2, 2, 27, 27, 27, 27, 27, 27, 27, 87, - 78, 78, 78, 78, 78, 79, 77, 65, 81, 79, 2, 2, 2, 2, 2, 2, - 82, 78, 43, 43, 43, 43, 78, 78, 65, 66, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 64, 43, 43, 43, 43, 65, 36, 36, - 36, 64, 43, 43, 77, 64, 43, 57, 2, 2, 2, 56, 43, 43, 43, 43, - 64, 43, 43, 77, 79, 43, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, - 43, 43, 43, 77, 43, 2, 66, 2, 58, 2, 2, 2, 2, 2, 2, 2, - 43, 43, 43, 43, 43, 43, 43, 79, 2, 36, 36, 36, 36, 36, 36, 36, - 43, 43, 43, 43, 77, 43, 43, 43, 77, 43, 79, 43, 43, 43, 43, 43, - 43, 43, 43, 64, 43, 43, 43, 43, 36, 36, 36, 36, 36, 78, 78, 78, - 43, 77, 79, 79, 36, 36, 36, 36, 36, 64, 77, 97, 2, 2, 2, 2, - 43, 82, 36, 36, 36, 36, 36, 36, 36, 36, 78, 43, 43, 43, 43, 78, - 77, 57, 2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 7, 43, 43, 43, - 27, 27, 84, 61, 61, 61, 53, 20,150, 61, 61, 61, 61, 61, 61, 61, - 61, 61, 61, 61, 61, 61, 61, 21, 65, 36, 36, 64, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 78, 79, 43, - 43, 43, 57, 2, 2, 2, 2, 2, 43, 43, 43, 57, 2, 2, 61, 61, - 40, 40, 89, 61, 61, 61, 61, 61, 7, 7, 7, 7, 7,168, 27, 27, - 27, 87, 36, 36, 36, 36, 36, 36, 40, 63, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 76,146, 2, 27, 27, 27, 30, 2, 2, 2, 2, - 82, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 79, - 43, 68, 40, 40, 40, 40, 40, 40, 40, 80, 43, 43, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 47, 57, 61, 61,169, 79, 43, 61,169, 78, - 78,170, 59, 59, 59, 75, 43, 43, 43, 70, 47, 43, 43, 43, 61, 61, - 61, 61, 61, 61, 61, 43, 43, 61, 61, 43, 70, 61, 61, 61, 61, 61, - 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 16, 11, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, - 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, - 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, - 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, - 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 16, 7, - 43, 43, 43, 70, 61, 47, 43, 43, 43, 43, 43, 43, 43, 43, 70, 61, - 61, 61, 47, 61, 61, 61, 61, 61, 61, 61, 70, 21, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 56, 43, 43, 16, 16, 16, 16, 16, 39, 16, 16, - 43, 43, 43, 68, 40, 40, 40, 40, 7, 7, 7, 7, 7, 7, 7, 71, - 7, 7, 7, 7, 7, 7, 7,171, 36, 36, 36, 36, 36, 76, 43, 43, - 172, 7, 7, 7, 7, 7, 7, 85, 16, 16, 43, 43, 43, 68, 40, 40, - 27, 27, 27, 27, 27, 27,145, 27,173, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27,145, 27, 27, 27, 27, 27, 27, 84, 61, - 61, 61, 61, 61, 61, 25, 41, 41, 0, 0, 29, 21, 21, 21, 23, 21, - 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, - 9, 22, 21, 18, 24, 16, 24, 5, 5, 5, 5, 22, 25, 18, 25, 0, - 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, 26, 25, 15, 15, - 24, 15, 7, 19, 15, 21, 9, 25, 9, 5, 5, 25, 5, 9, 5, 7, - 7, 7, 9, 8, 8, 5, 7, 5, 6, 6, 24, 24, 6, 24, 12, 12, - 6, 5, 9, 21, 25, 9, 26, 12, 11, 11, 9, 6, 5, 21, 17, 17, - 17, 26, 26, 23, 23, 12, 17, 12, 21, 12, 12, 21, 7, 21, 1, 1, - 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, - 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, 21, 1, 24, 7, 1, 12, - 7, 6, 12, 10, 10, 10, 10, 12, 21, 6, 10, 7, 7, 10, 23, 7, - 15, 26, 13, 21, 13, 7, 15, 7, 12, 23, 21, 26, 21, 15, 17, 7, - 29, 7, 7, 22, 18, 18, 14, 14, 14, 7, 10, 21, 17, 21, 11, 12, - 5, 6, 8, 8, 8, 24, 5, 24, 9, 24, 29, 29, 29, 1, 20, 19, - 22, 20, 27, 28, 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, 25, 22, - 18, 21, 21, 29, 15, 6, 18, 6, 12, 11, 9, 26, 26, 9, 26, 5, - 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, 26, 22, 18, 26, 18, 25, - 18, 22, 5, 12, 22, 21, 21, 22, 18, 17, 26, 6, 7, 14, 17, 22, - 26, 14, 17, 6, 14, 6, 12, 24, 24, 6, 26, 15, 6, 21, 11, 21, - 24, 9, 6, 9, 23, 26, 6, 10, 4, 4, 3, 3, 7, 25, 17, 16, - 16, 22, 16, 16, 25, 17, 7, 1, 25, 24, 26, 1, 2, 2, 12, 15, - 21, 14, 7, 15, 9, 12, 12, 17, 13, 15, 26, 10, 10, 1, 13, 23, - 7, 13, 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, - 11, 12, 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 20, 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 35, 0, 0, 0, 0, 36, 0, 37, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, - 0, 0, 0, 0, 41, 42, 43, 0, 44, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, - 4, 5, 6, 7, 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, 16, 17, - 16, 18, 16, 19, 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, 21, 19, - 0, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, - 0, 35, 0, 0, 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, - 0, 0, 0, 0, 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, 0, 0, - 0, 0, 0, 55, 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, 60, 61, - 62, 63, 0, 0, 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, 67, 0, - 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 69, 0, 0, 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, 0, 0, - 0, 0, 0, 0, 0, 0, 74, 75, 0, 0, 0, 0, 76, 77, 0, 78, - 79, 0, 0, 80, 81, 0, 82, 62, 0, 83, 84, 0, 0, 85, 86, 87, - 0, 88, 0, 89, 0, 90, 0, 0, 51, 91, 51, 0, 92, 0, 93, 0, - 0, 0, 81, 0, 0, 0, 94, 95, 0, 96, 97, 98, 99, 0, 0, 0, - 0, 0, 51, 0, 0, 0, 0,100,101, 0, 0, 0, 0, 0, 0,102, - 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0, 0, 0,104,105, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,106, 0, 0,107, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,108,109, 0, 0,110, 0, 0, 0, 0, - 0, 0,111, 0,112, 0,105, 0, 0, 0, 0, 0,113,114, 0, 0, - 0, 0, 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0,117, 0,118, - 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 0, - 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, 14, 15, - 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, 0, 0, - 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 0, 0, 28, 29, - 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, 0, 35, 33, 0, - 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, 0, 0, - 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, 0, 43, - 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, - 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 52, 0, 53, - 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, 0, 0, - 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, 0, 0, 0, 61, 52, 0, - 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, 0, 0, 0, 67, 0, 68, - 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, 0, 0, 77, 78, 0, 0, - 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, 0, 81, 0, 0, 0, 0, - 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, 84, 0, 85, 0, 52, 0, - 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, 0, 0, 0, 88, 57, 0, - 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, 0, 0, 33, 0, 0, 91, - 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, 0, 0, 93, 0, 0, 0, - 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, - 99, 0, 0, 0,100, 0, 0, 0, 0,101,102, 93, 0, 0,103, 0, - 0, 0, 84, 0, 0,104, 0, 0, 0,105,106, 0, 0,107,108, 0, - 0, 0, 0, 0, 0,109, 0, 0,110, 0, 0, 0, 0,111, 33, 0, - 112,113,114, 57, 0, 0,115, 35, 0, 0,116, 0, 0, 0,117, 0, - 0, 0, 0, 0, 0,118, 0, 0,119, 0, 0, 0, 0,120, 88, 0, - 0, 0, 0, 0, 57, 0, 0, 0, 0, 52,121, 0, 0, 0, 0,122, - 0, 0,123, 0, 0, 0, 0,121, 0, 0,124, 0, 0, 0, 0, 0, - 79, 0, 0, 0, 0,125, 0, 0, 0,126, 0, 0, 0,127, 0,128, - 0, 0, 0, 0,129,130,131, 0,132, 0,133, 0, 0, 0,134,135, - 136, 0, 77, 0, 0, 0, 0, 0, 35, 0, 0, 0,137, 0, 0, 0, - 138, 0, 0, 0,139, 0, 0,140, 0, 0,141, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 4, 4, 8, - 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, 1, 1, 19, 1, - 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, 29, 30, - 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, 1, 36, 37, 0, - 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, 0, 0, 43, 36, - 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, 0, 47, 0, 38, - 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, 0, 19, 52, 1, 0, 0, - 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, 35, 1, 0, 0, - 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, 0, 59, 0, 60, - 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, 64, 0, 0, 0, - 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 69, - 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, 0, 0, 0, 78, - 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, 0, 0, 0, 62, - 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, 83, 0, 0, 19, - 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, 15, 86, 36, 10, - 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, - 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, 87, 9, - 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, 21, 1, 21, 92, 93, 1, - 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, 81, 99,100, 4, 58, 0, - 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, 0, 0, - 101,102, 0, 0,103, 0, 0, 1, 1, 50, 0, 0, 0, 38, 0, 63, - 0, 0, 0, 0, 0, 62, 0, 0,104, 68, 61, 0, 0, 0, 78, 0, - 0, 0,105,106, 58, 38, 81, 0, 0, 0, 0, 0, 0,107, 1, 14, - 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, 0, 0, 0,108, 0, 0, - 109, 61, 0,110, 0, 0, 0, 1, 0, 0, 0, 0, 49, 50, 0, 0, - 19, 58, 0, 0, 0, 51, 0,111, 14, 52,112, 41, 0, 0, 62, 0, - 0, 61, 0, 0,113, 0, 87, 0, 0, 0, 61, 62, 0, 0, 62, 0, - 89, 0, 0,113, 0, 0, 0, 0,114, 0, 0, 0, 78, 55, 0, 38, - 1, 58, 1, 58, 0, 0, 0, 0, 0, 88, 63, 89, 0, 0,115, 0, - 0, 0, 55, 0, 0, 0, 0,115, 0, 0, 0, 0, 61, 0, 0, 0, - 0, 79, 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, - 0, 0, 8, 91, 0, 0, 1, 87, 0, 0,116, 0, 0, 0, 0, 0, - 0,117, 0,118,119,120,121, 0,104, 4,122, 49, 23, 0, 0, 0, - 38, 50, 38, 58, 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, 48,105, - 87, 0, 0, 0, 0, 1, 0, 0, 0,123, 0, 0, 0,112, 4,122, - 0, 0, 0, 1,124, 0, 0, 0, 0, 0,230,230,230,230,230,232, - 220,220,220,220,232,216,220,220,220,220,220,202,202,220,220,220, - 220,202,202,220,220,220, 1, 1, 1, 1, 1,220,220,220,220,230, - 230,230,230,240,230,220,220,220,230,230,230,220,220, 0,230,230, - 230,220,220,220,220,230,232,220,220,230,233,234,234,233,234,234, - 233,230, 0, 0, 0,230, 0,220,230,230,230,230,220,230,230,230, - 222,220,230,230,220,220,230,222,228,230, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, 25, 0,230,220, - 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, - 34,230,230,220,220,230,220,230,230,220, 35, 0, 0, 0, 0, 0, - 230,230,230, 0, 0,230,230, 0,220,230,230,220, 0, 0, 0, 36, - 0, 0,230,220,230,230,220,220,230,220,220,230,220,230,220,230, - 230, 0, 0,220, 0, 0,230,230, 0,230, 0,230,230,230,230,230, - 0, 0, 0,220,220,220,230,220,220,220,230,230, 0,220, 27, 28, - 29,230, 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230,230, 0, - 0, 0, 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, - 0, 0, 0, 0, 9, 0,103,103, 9, 0,107,107,107,107,118,118, - 9, 0,122,122,122,122,220,220, 0, 0, 0,220, 0,220, 0,216, - 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130,130,130, - 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, 0, 0, - 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, 0,230, 0, 0, 0,228, - 0, 0, 0,222,230,220,220, 0, 0, 0,230, 0, 0,220,230,220, - 0,220,230,230,230, 0, 0, 0, 9, 9, 0, 0, 7, 0,230, 0, - 1, 1, 1, 0, 0, 0,230,234,214,220,202,230,230,230,230,230, - 232,228,228,220,218,230,233,220,230,220,230,230, 1, 1, 1, 1, - 1,230, 0, 1, 1,230,220,230, 1, 1, 0, 0,218,228,232,222, - 224,224, 0, 8, 8, 0, 0, 0, 0,220,230, 0,230,230,220, 0, - 0,230, 0, 0, 26, 0, 0,220, 0,230,230, 1,220, 0, 0,230, - 220, 0, 0, 0,220,220, 0, 0,230,220, 0, 9, 7, 0, 0, 7, - 9, 0, 0, 0, 9, 7, 6, 6, 0, 0, 0, 0, 1, 0, 0,216, - 216, 1, 1, 1, 0, 0, 0,226,216,216,216,216,216, 0,220,220, - 220, 0,232,232,220,230,230,230, 7, 0, 16, 17, 17, 33, 17, 49, - 17, 17, 84, 97,135,145, 26, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17,177, 0, 1, 2, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, - 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, 6, 7, 8, 3, 3, 3, - 3, 3, 9, 10, 11, 12, 13, 3, 3, 3, 3, 3, 3, 3, 3, 14, - 3, 15, 3, 3, 3, 3, 3, 3, 16, 17, 18, 19, 20, 21, 3, 3, - 3, 22, 23, 24, 3, 3, 3, 3, 3, 3, 25, 3, 3, 3, 3, 3, - 3, 3, 3, 26, 3, 3, 27, 28, 0, 1, 0, 0, 0, 0, 0, 1, - 0, 2, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, - 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 8, 9, 0, 0, 0, 0, 0, 0, 9, 0, 9, 0, 0, 0, 0, - 0, 0, 0, 10, 11, 12, 13, 0, 0, 14, 15, 16, 6, 0, 17, 18, - 19, 19, 19, 20, 21, 22, 23, 24, 19, 25, 0, 26, 27, 19, 19, 28, - 29, 30, 0, 31, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 19, - 28, 0, 32, 33, 9, 34, 35, 19, 0, 0, 36, 37, 38, 39, 40, 19, - 0, 41, 42, 43, 44, 31, 0, 1, 45, 42, 0, 0, 0, 0, 0, 32, - 14, 14, 0, 0, 0, 0, 14, 0, 0, 46, 47, 47, 47, 47, 48, 49, - 47, 47, 47, 47, 50, 51, 52, 53, 43, 21, 0, 0, 0, 0, 0, 0, - 0, 54, 6, 55, 0, 14, 19, 1, 0, 0, 0, 0, 56, 57, 0, 0, - 0, 0, 0, 19, 58, 31, 0, 0, 0, 0, 0, 0, 0, 59, 14, 0, - 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 60, 61, 0, - 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 3, 0, 4, - 5, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 1, 1, 0, 0, 8, - 9, 0, 8, 9, 0, 0, 0, 0, 8, 9, 10, 11, 12, 0, 0, 0, - 13, 0, 0, 0, 0, 14, 15, 16, 17, 0, 0, 0, 1, 0, 0, 18, - 19, 0, 0, 0, 20, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, - 1, 1, 1, 1, 0, 8, 21, 9, 0, 0, 22, 0, 0, 0, 0, 1, - 0, 23, 24, 25, 0, 0, 26, 0, 0, 0, 8, 21, 27, 0, 1, 0, - 0, 1, 1, 1, 1, 0, 1, 28, 29, 30, 0, 31, 32, 20, 1, 1, - 0, 0, 0, 8, 21, 9, 1, 4, 5, 0, 0, 0, 33, 9, 0, 1, - 1, 1, 0, 8, 21, 21, 21, 21, 34, 1, 35, 21, 21, 21, 9, 36, - 0, 0, 37, 38, 1, 0, 39, 0, 0, 0, 1, 0, 1, 0, 0, 0, - 0, 8, 21, 9, 1, 0, 0, 0, 40, 0, 8, 21, 21, 21, 21, 21, - 21, 21, 21, 9, 0, 1, 1, 1, 1, 8, 21, 21, 21, 9, 0, 0, - 0, 41, 0, 42, 43, 0, 0, 0, 1, 44, 0, 0, 0, 45, 8, 9, - 1, 0, 0, 0, 8, 21, 21, 21, 9, 0, 1, 0, 1, 1, 8, 21, - 21, 9, 0, 4, 5, 8, 9, 1, 0, 0, 0, 1, 2, 3, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 3, 3, 3, 3, 3, 3, - 3, 15, 3, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 0, 0, 1, 2, 3, - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 17, 17, - 18, 17, 19, 20, 21, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 24, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 25, 25, 26, 27, - 28, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 52, 53, 31, - 31, 31, 31, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 56, 57, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 58, 31, 31, 31, - 59, 60, 61, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 64, 65, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 66, 67, 68, 31, 31, 31, 31, 69, 31, 31, 31, 31, 31, - 31, 31, 17, 70, 71, 72, 17, 17, 73, 74, 31, 75, 76, 77, 78, 79, - 80, 31, 81, 82, 17, 83, 17, 17, 17, 17, 31, 31, 23, 23, 23, 23, - 23, 23, 23, 84, 31, 31, 31, 31, 23, 84, 31, 31, 23, 23, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 85, 0, 0, 1, - 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, - 4, 4, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 11, - 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 19, 27, - 28, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, - 37, 37, 38, 38, 39, 40, 41, 41, 42, 42, 42, 43, 44, 44, 45, 46, - 47, 47, 47, 47, 48, 48, 48, 48, 48, 48, 49, 50, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, 51, - 60, 61, 62, 63, 64, 65, 66, 7, 67, 67, 68, 69, 70, 71, 72, 73, - 74, 75, 76, 7, 4, 4, 4, 4, 77, 77, 77, 77, 78, 79, 80, 81, - 82, 83, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 85, 85, 85, - 0, 0, 0, 0, 86, 87, 88, 88, 89, 90, 48, 91, 0, 0, 92, 92, - 92, 92, 92, 93, 94, 95, 96, 97, 98, 47, 99,100,101,102, 0,103, - 104,105, 0, 0, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, - 92, 92, 92, 0,106,106,106,106,106,106,106,106,106,106,106,107, - 108,108,108,108,108, 11,109,110,111, 4,112, 4,113,114,115,116, - 117,118,119,120,121,122,123,124,125,126, 50,127, 47, 47, 47, 47, - 47, 47, 47, 47,128,128,128,128,128,128,128,128,128,128,128,128, - 92, 92, 92, 92, 92, 92, 92, 92,129,130, 19, 19, 19, 19, 19, 19, - 131, 19, 19, 19,132,133, 19,134,135,136,137,101,138,138,138,138, - 0, 77,139,140,128,128,141,142,143,144,145,146,147,148,149,150, - 151,152,153,154,155,155,155,155,155,155, 4, 4,156,157,158,159, - 160,161,162,163,164,165,166,167,168,169,170,170,171,171,172,172, - 173,174,174,174, 19, 19,175,176,177,178,179,180,181,181,182,183, - 184,185,186,187,188,188,189,190,191,192,193,193,194,194,195,195, - 128,128,196,196,197,198,199,200,201,201,128,128,202,202,203,203, - 204,204,205,205,206,207,208,209, 28, 28,210,210,211,212,213,213, - 214,215,216,216,128,128,217,217,218,218,219, 34,220,220,220,220, - 220,220,220,220,220,220,220,220,220,220,128,128,128,128,128,128, - 128,128,221,221,222,222,222,222,222,222,222,222,223,223,223,223, - 223,223,223,223,223,223,128,128,128,128,128,128,128,128,128,128, - 224,224,128,128,110,110,110,110,110,110,110,110,110,225,226,227, - 228,228,228,228,128,128,128,128,229,229,128,128,230,230,230,230, - 231,231,231,232,233,233,233,233,233,233,233,233,233,233,233,233, - 234,234,234,234,234,234,234,234,233,233,128,128,128,128,128,128, - 128,128,104,104,235,236,236,236,237,238,239,239,239,239,239,239, - 128,128,128,128,240,240,241, 0,128,128,128,128, 0, 0, 0, 0, - 7,242, 0, 0, 0, 0, 0, 0, 0,243,244, 0, 77, 77, 0, 0, - 0, 0,128,128,245,245,245,245,245,245,245,245,245,245,245,245, - 128,128,128,128,128,128,128,128, 4, 4,128,128,246, 11, 11, 11, - 247,247,128,128,128,128,248,249,128,128,128,128,128,128,250,250, - 128,128,251,251,128,128,128,128,128,128, 48, 48,252,252,252,252, - 253,253,128,128, 0, 0, 0, 0, 0, 0,128,128, 19, 19, 19, 19, - 128,128,128,128,254, 0,128,128, 0, 0, 0, 0, 92, 92,128,128, - 128,128,128,128, 0, 0,128,128, 7, 7, 7, 7, 0, 0, 0, 0, - 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, 4, 4, 4, 4, - 4, 4, 4, 6, 0, 0, 7, 0, 8, 8, 8, 8, 8, 8, 8, 9, - 10, 11, 11, 11, 11, 11, 12, 11, 13, 13, 13, 13, 14, 13, 13, 13, - 13, 13, 13, 15, 16, 16, 16, 16, 16, 17, 18, 18, 18, 18, 18, 18, - 19, 20, 21, 21, 22, 23, 21, 24, 21, 21, 21, 21, 21, 25, 21, 21, - 26, 26, 26, 26, 26, 21, 21, 21, 27, 27, 27, 27, 28, 28, 28, 28, - 29, 29, 29, 29, 30, 30, 26, 21, 21, 21, 31, 21, 32, 32, 32, 32, - 32, 33, 34, 32, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, - 38, 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, - 42, 42, 42, 42, 43, 43, 43, 43, 44, 44, 44, 45, 44, 44, 44, 44, - 46, 46, 46, 46, 47, 47, 47, 47, 47, 48, 47, 47, 49, 49, 49, 49, - 49, 49, 50, 50, 50, 50, 50, 51, 52, 52, 52, 52, 53, 53, 53, 53, - 53, 53, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, 57, 57, - 57, 57, 58, 57, 59, 59, 60, 61, 62, 62, 63, 63, 64, 64, 64, 64, - 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 55, 67, 67, 67, 67, - 67, 68, 68, 68, 69, 69, 69, 69, 69, 69, 64, 64, 70, 70, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 8, 72, 72, 72, 72, 73, 73, 73, 73, - 74, 74, 74, 74, 75, 75, 75, 75, 75, 76, 76, 76, 13, 50, 50, 50, - 73, 77, 78, 79, 4, 4, 80, 4, 4, 81, 82, 83, 4, 4, 4, 84, - 11, 11, 11, 11, 85, 0, 0, 0, 0, 0, 0, 86, 0, 4, 0, 0, - 0, 8, 8, 8, 0, 0, 87, 88, 89, 0, 4, 4, 6, 0, 0, 0, - 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 4, 4, 92, 92, 92, 92, - 50, 50, 50, 93, 93, 93, 93, 93, 53, 53, 13, 13, 94, 94, 94, 94, - 94, 94, 94, 0, 95, 0, 96, 97, 98, 99, 99, 99, 99,100,101,102, - 102,102,102,103,104,104,104,105, 52, 0,104,104, 0, 0, 0,102, - 52, 52, 0, 0, 0, 0, 52,106, 0,102,102,107,102,102,102,102, - 102,108, 0, 0,109,109,109,109,109,110,110,110,111,111,111,111, - 13, 13,112,112,112,112,112,112, 0, 0,113, 4,114, 4, 4, 4, - 115,115,115, 0,116,116,116,116,117,117,117,117,117,117, 32, 32, - 118,118,119,120,120,120, 52, 52,121,121,121,121,122,121, 49, 49, - 123,123,123,123,123,123, 49, 49,124,124,124,124,124,124,125,125, - 53, 53, 53, 4, 4,126,127, 54,125,125,125,125,128,128,128,128, - 4,129, 18, 18, 18, 21, 21, 21, 21, 21, 21,130, 8, 0,131, 0, - 0, 0, 0, 21, 21, 21, 21,132, 0, 0, 1, 2, 1, 2,133,101, - 102,134, 52, 52,135,135,135,135, 11, 0, 11, 11, 11, 0, 0,136, - 137,137,138,138,138,138,139, 0,140,140,140,141,141,142,142,142, - 143,143,144,144,144,144,144,144,145,145,145,145,145,146,146,146, - 147,147,147,148,148,148,148,148,149,149,149,150,150,150,150,151, - 151,151,151,151,152,152,152,152,153,153,153,153,154,154,154,154, - 155,155,156,156,157,157,157,157,157,157,158,158,159,159,160,160, - 160,160,160,160,161,161,162,162,162,162,162,162,163,163,163,163, - 163,163,164,164,165,165,165,165,166,166,166,166,167,167,167,167, - 168,168,169,169,170,170,170,170,171,171,171,171,172,172,172,172, - 173,173,173,173,174,174,174,174,175,175,175,175,176, 21, 21, 21, - 177,177,177,178,178,178,178,179,179,179,179,180,180,180,181,181, - 182,182,182,182,183,183,183,183,183,184,184,184,185,185,185,185, - 185,186,186,186,187,187,187,187,187,187,188, 43,189,189,189,189, - 190,190,190,191,191,191,191,191,192,192,192,193,192,192,192,192, - 194,194,194,194,195,195,195,195,196,196,196,196,197,197,197,197, - 198,198,198,198,198,198, 66, 66,199,199,199,199,199, 49, 49, 49, - 200,200,200,200,201,201,201,201,202,202,202,202,203,203,203,203, - 204,204,204,204,205,205,205,205,205,206,206,206,206,206,206, 55, - 207,207,207,207,208,208,208,208,209,209,209,209,209,209,209,210, - 210,210,210,210,211,211,211,211,211,211,212,212,212,212,212,212, - 213,213,213,213,214,214,214,214,110,110,110,110,215,215,215,215, - 216,216,216,216,217,217,217,217,218,218,218,218,219,219,219,219, - 220,220,220,221,221,221,221,221,221,222,222,222,223,223,223,223, - 224,224,224,224,225,225,225,225,226,226,226,226,226,226,227, 94, - 228,228,228,228,229,229,229,229,230, 99, 99, 99, 99, 99, 99, 99, - 99, 99,102,231, 99,232,102,233,233,233,233,233,234,234,234,234, - 234,234, 0, 0, 8, 0, 0, 0, 0, 0,235,236,237, 0,238, 0, - 239,239,239,239, 91, 91, 91, 13,240,240,240,240,241,241,241,241, - 242,242,242,242,243,243,243,243,244,244,244,244,245,245,245,245, - 246,246,246,246,247, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, - 2, 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, 2, 2, - 2, 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, 8, 10, - 8, 11, 8, 8, 8, 8, 8, 8, 12, 13, 13, 13, 14, 14, 14, 14, - 14, 15, 14, 14, 16, 17, 17, 17, 17, 17, 17, 17, 18, 19, 19, 19, - 19, 19, 19, 19, 20, 21, 20, 22, 20, 20, 23, 23, 20, 20, 20, 20, - 22, 20, 24, 7, 7, 25, 20, 20, 26, 20, 20, 20, 20, 20, 20, 21, - 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, - 31, 31, 31, 31, 32, 20, 20, 20, 33, 33, 33, 33, 34, 35, 33, 33, - 33, 36, 33, 33, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, - 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, - 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 47, - 48, 48, 48, 48, 49, 49, 49, 49, 49, 50, 51, 49, 52, 52, 52, 52, - 53, 53, 53, 53, 53, 53, 54, 53, 55, 55, 55, 55, 56, 56, 56, 56, - 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 60, - 60, 60, 61, 62, 63, 63, 63, 63, 64, 64, 64, 64, 64, 65, 0, 0, - 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 70, 71, 71, - 71, 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, - 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, - 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 7, 7, 7, - 83, 7, 84, 85, 0, 84, 86, 0, 2, 87, 88, 2, 2, 2, 2, 89, - 90, 87, 91, 2, 2, 2, 92, 2, 2, 2, 2, 93, 0, 0, 0, 86, - 1, 0, 0, 94, 0, 95, 96, 0, 4, 0, 0, 0, 0, 0, 0, 4, - 97, 97, 97, 97, 98, 98, 98, 98, 13, 13, 13, 13, 99, 99, 99, 99, - 100,100,100,100, 0,101, 0, 0,102,100,103,104, 0, 0,100, 0, - 105,106,106,106,106,106,106,106,106,106,107,105,108,109,109,109, - 109,109,109,109,109,109,110,108,111,111,111,111,112, 55, 55, 55, - 55, 55, 55,113,109,109,109,110,109,109, 0, 0,114,114,114,114, - 115,115,115,115,116,116,116,116,117,117,117,117, 96, 2, 2, 2, - 2, 2, 94, 2,118,118,118,118,119,119,119,119,120,120,120,120, - 121,121,121,121,121,121,121,122,123,123,123,123,124,124,124,124, - 124,124,124,125,126,126,126,126,127,127,127,127,128,128,128,128, - 2, 2, 3, 2, 2,129,130, 0,131,131,131,131,132, 17, 17, 18, - 20, 20, 20,133, 7, 7, 7,134, 20, 20, 20, 23, 0,135,109,109, - 109,109,109,136,137,137,137,137, 0, 0, 0,138,139,139,139,139, - 140,140,140,140, 84, 0, 0, 0,141,141,141,141,142,142,142,142, - 143,143,143,143,144,144,144,144,145,145,145,145,146,146,146,146, - 147,147,147,147,148,148,148,148,149,149,149,149,150,150,150,150, - 151,151,151,151,152,152,152,152,153,153,153,153,154,154,154,154, - 155,155,155,155,156,156,156,156,157,157,157,157,158,158,158,158, - 159,159,159,159,160,160,160,160,161,161,161,161,162,162,162,162, - 163,163,163,163,164,164,164,164,165,165,165,165,166,166,166,166, - 167,167,167,167,168,168,168,168,169,169,169,169,170,170,170,170, - 171,171,171,171,172,172,172,172,173,173,173,173,174,174,174,174, - 175,175,175,175,176,176,176,176,177, 20, 20, 20,178,178,178,178, - 179,179,179,179,180,180,180,180,181,181,181,181,182,182,182,182, - 183,183,183,183,184,184,184,184,185,185,185,185,186,186,186,186, - 187,187,187,187,188,188,188,188,189, 45, 45, 45,190,190,190,190, - 191,191,191,191,192,192,192,192,193,193,193,193,193,193,194,193, - 195,195,195,195,196,196,196,196,197,197,197,197,198,198,198,198, - 199,199,199,199,200,200,200,200,201,201,201,201,202,202,202,202, - 203,203,203,203,204,204,204,204,205,205,205,205,206,206,206,206, - 207,207,207,207,208,208,208,208,209,209,209,209,210,210,210,210, - 211,211,211,211,212,212,212,212,213,213,213,213,214,214,214,214, - 215,215,215,215,216,216,216,216,217,217,217,217,218,218,218,218, - 219,219,219,219,220,220,220,220,221,221,221,221,222,222,222,222, - 223,223,223,223,224,224,224,224,225,225,225,225,226,226,226,226, - 227,227,227,227,228,229,229,229,230,230,230,230,229,229,229,229, - 231,106,106,106,232,106,106,106,106,233,109,109,234,234,234,234, - 235,235,235,235, 0,236, 86, 0, 0, 0,236, 7, 82,138, 7, 0, - 0, 0,237, 86,238,238,238,238,239,239,239,239,240,240,240,240, - 241,241,241,241,242,242,242,242,243,243,243,243,244,244,244,244, - 245,245,245,245,246, 0, 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, 19, 0, 0, 0, - 0, 0, 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, - 9, 9, 0, 9, 9, 0, 9, 0, 9, 9, 55, 55, 55, 55, 55, 55, - 6, 6, 6, 6, 6, 1, 1, 6, 6, 4, 4, 4, 4, 4, 4, 4, - 4, 14, 14, 14, 14, 14, 14, 14, 3, 3, 3, 3, 3, 0, 3, 3, - 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, 1, 1, 3, 3, - 1, 3, 3, 3, 37, 37, 37, 37, 38, 38, 38, 38, 64, 64, 64, 64, - 90, 90, 90, 90, 95, 95, 95, 95, 3, 3, 0, 3, 7, 7, 7, 7, - 7, 1, 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, - 11, 11, 11, 11, 10, 10, 10, 10, 21, 21, 21, 21, 22, 22, 22, 22, - 23, 23, 23, 23, 16, 16, 16, 16, 20, 20, 20, 20, 36, 36, 36, 36, - 24, 24, 24, 24, 24, 24, 24, 0, 18, 18, 18, 18, 25, 25, 25, 25, - 25, 0, 0, 0, 0, 25, 25, 25, 33, 33, 33, 33, 8, 8, 8, 8, - 8, 8, 8, 0, 12, 12, 12, 12, 30, 30, 30, 30, 29, 29, 29, 29, - 28, 28, 28, 28, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 0, - 0, 0, 35, 35, 45, 45, 45, 45, 44, 44, 44, 44, 44, 0, 0, 0, - 43, 43, 43, 43, 46, 46, 46, 46, 31, 31, 31, 31, 32, 32, 0, 0, - 32, 0, 32, 32, 32, 32, 32, 32, 48, 48, 48, 48, 52, 52, 52, 52, - 58, 58, 58, 58, 54, 54, 54, 54, 91, 91, 91, 91, 62, 62, 62, 62, - 76, 76, 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, 73, 73, 73, 73, - 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, - 1, 1, 0, 0, 19, 19, 9, 9, 9, 9, 9, 6, 19, 9, 9, 9, - 9, 9, 19, 19, 9, 9, 9, 19, 6, 19, 19, 19, 19, 19, 19, 9, - 0, 0, 0, 19, 0, 0, 9, 0, 0, 0, 19, 19, 27, 27, 27, 27, - 56, 56, 56, 56, 61, 61, 61, 61, 13, 13, 13, 13, 0, 13, 0, 13, - 0, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 0, 15, 15, 15, - 15, 15, 15, 15, 15, 1, 1, 0, 0, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 0, 26, 26, 26, 26, 26, 12, 12, 12, 12, 12, 12, 0, - 39, 39, 39, 39, 86, 86, 86, 86, 77, 77, 77, 77, 79, 79, 79, 79, - 60, 60, 60, 60, 65, 65, 65, 65, 75, 75, 75, 75, 69, 69, 69, 69, - 69, 69, 0, 69, 74, 74, 74, 74, 84, 84, 84, 84, 84, 84, 84, 0, - 68, 68, 68, 68, 92, 92, 92, 92, 87, 87, 87, 87, 19, 9, 19, 19, - 19, 19, 0, 0, 2, 2, 2, 2, 19, 19, 19, 4, 3, 3, 0, 0, - 1, 1, 6, 6, 0, 0, 17, 17, 17, 17, 0, 0, 49, 49, 49, 49, - 0, 1, 1, 1, 71, 71, 71, 71, 67, 67, 67, 67, 42, 42, 42, 42, - 41, 41, 41, 41,118,118,118,118, 53, 53, 53, 53, 59, 59, 59, 59, - 40, 40, 40, 40, 51, 51, 51, 51, 50, 50, 50, 50,135,135,135,135, - 106,106,106,106,104,104,104,104,161,161,161,161,170,170,170,170, - 110,110,110,110, 47, 47, 47, 47, 81, 81, 81, 81,120,120,120,120, - 116,116,116,116,128,128,128,128, 66, 66, 66, 66, 72, 72, 72, 72, - 98, 98, 98, 98, 97, 97, 97, 97, 57, 57, 57, 57, 88, 88, 88, 88, - 117,117,117,117,112,112,112,112, 78, 78, 78, 78, 83, 83, 83, 83, - 82, 82, 82, 82,122,122,122,122, 89, 89, 89, 89,130,130,130,130, - 144,144,144,144,165,165,165,165,156,156,156,156,156,156, 3, 3, - 147,147,147,147,148,148,148,148,158,158,158,158,153,153,153,153, - 149,149,149,149, 94, 94, 94, 94, 85, 85, 85, 85,101,101,101,101, - 96, 96, 96, 96,111,111,111,111,100,100,100,100,100, 36, 36, 36, - 108,108,108,108,129,129,129,129,109,109,109,109,107,107,107,107, - 107,107,107, 1,171,171,171,171,137,137,137,137,124,124,124,124, - 123,123,123,123,114,114,114,114,102,102,102,102,126,126,126,126, - 142,142,142,142,125,125,125,125,154,154,154,154,150,150,150,150, - 141,141,141,141,140,140,140,140,121,121,121,121,169,169,169,169, - 133,133,133,133,134,134,134,134,138,138,138,138,143,143,143,143, - 145,145,145,145,163,163,163,163, 63, 63, 63, 63,157,157,157,157, - 80, 80, 80, 80,127,127,127,127,166,166,166,166,115,115,115,115, - 159,159,159,159,103,103,103,103,119,119,119,119,167,167,167,167, - 146,146,146,146, 99, 99, 99, 99,136,139, 13, 13,155,155,155,155, - 136,136,136,136, 17, 15, 15, 15, 17, 17, 15, 15, 15, 17, 17, 17, - 139,139,139,139,105,105,105,105, 0, 0, 0, 1, 0, 0, 1, 1, - 131,131,131,131,151,151,151,151,160,160,160,160,152,152,152,152, - 164,164,164,164,168,168,168,168,113,113,113,113,132,132,132,132, - 15, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, - 9, 10, 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 16, 17, 9, 9, 9, 9, 18, 9, 9, 9, 9, 9, 19, 20, 21, 9, - 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 23, 9, 9, 9, 9, 9, 24, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, - 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, - 0, 0, 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, - 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, - 0, 0, 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, - 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, - 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, - 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, - 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100, - 101,102,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0,107, 0, - 0, 0,108, 0,109, 0,110, 0,111,112,113, 0,114, 0, 0, 0, - 115, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,118,119,120,121, 0,122,123,124,125,126, - 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,128,129,130,131,132,133,134,135,136,137,138,139,140,141, - 142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157, - 0, 0, 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,162, 0, - 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,164,165, 0, 0, 0, - 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,167, 0, 0, 0,168,169, 0, 0,170, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,171, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,172, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,175, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,176,177, 0, 0, 0, 0,178,179, 0, - 0, 0,180,181,182,183,184,185,186,187,188,189,190,191,192,193, - 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209, - 210,211,212,213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, - 3, 4, -}; -static const uint16_t -_hb_ucd_u16[5080] = -{ - 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, - 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, - 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, - 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, - 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, - 13, 13, 13, 35, 9, 42, 11, 11, 43, 44, 32, 45, 46, 47, 47, 48, - 49, 50, 47, 47, 51, 32, 52, 53, 47, 47, 47, 47, 47, 54, 55, 56, - 57, 58, 47, 32, 59, 47, 47, 47, 47, 47, 60, 53, 61, 47, 62, 63, - 47, 64, 65, 66, 47, 67, 47, 47, 68, 69, 47, 47, 70, 32, 71, 32, - 72, 47, 47, 73, 74, 75, 76, 77, 78, 47, 47, 79, 80, 81, 82, 83, - 84, 47, 47, 85, 86, 87, 88, 89, 84, 47, 47, 79, 90, 47, 82, 91, - 92, 47, 47, 93, 94, 95, 82, 96, 97, 47, 47, 98, 99, 100, 101, 102, - 103, 47, 47, 104, 105, 106, 82, 107, 108, 47, 47, 93, 109, 110, 82, 111, - 112, 47, 47, 113, 114, 115, 82, 116, 92, 47, 47, 47, 117, 118, 101, 119, - 47, 47, 47, 120, 121, 122, 66, 66, 47, 47, 47, 123, 124, 125, 47, 47, - 126, 127, 128, 129, 47, 47, 47, 130, 131, 32, 32, 132, 133, 134, 66, 66, - 47, 47, 135, 136, 122, 137, 138, 139, 140, 141, 9, 9, 9, 11, 11, 142, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 143, 144, 145, - 47, 146, 9, 9, 9, 9, 9, 147, 148, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 149, 47, 150, 151, 47, 47, 47, 47, 152, 153, - 47, 154, 47, 155, 47, 156, 47, 156, 47, 47, 47, 157, 158, 159, 160, 145, - 161, 160, 47, 47, 162, 47, 47, 47, 163, 47, 164, 47, 47, 47, 47, 47, - 47, 47, 165, 166, 167, 47, 47, 47, 47, 47, 47, 47, 47, 168, 146, 146, - 47, 169, 47, 47, 47, 170, 171, 172, 160, 160, 173, 174, 32, 32, 32, 32, - 175, 47, 47, 176, 177, 122, 178, 179, 180, 47, 181, 61, 47, 47, 182, 183, - 47, 47, 184, 185, 186, 61, 47, 187, 188, 9, 9, 9, 66, 189, 190, 191, - 11, 11, 192, 27, 27, 27, 193, 194, 11, 195, 27, 27, 32, 32, 32, 32, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 196, 13, 13, 13, 13, 13, 13, - 197, 197, 197, 197, 197, 198, 197, 11, 199, 199, 199, 200, 201, 202, 202, 201, - 203, 204, 205, 206, 207, 208, 209, 210, 211, 27, 212, 212, 212, 213, 214, 32, - 215, 216, 217, 218, 219, 145, 220, 220, 221, 222, 223, 146, 224, 225, 146, 226, - 227, 227, 227, 227, 227, 227, 227, 227, 228, 146, 229, 146, 146, 146, 146, 230, - 146, 231, 227, 232, 146, 233, 234, 146, 146, 146, 146, 146, 146, 146, 145, 145, - 145, 235, 146, 146, 146, 146, 236, 145, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 237, 238, 146, 146, 239, 146, 146, 146, 146, 146, 146, 240, 146, - 146, 146, 146, 146, 146, 146, 241, 242, 145, 243, 146, 146, 244, 227, 245, 227, - 246, 247, 227, 227, 227, 248, 227, 249, 146, 146, 146, 227, 250, 146, 146, 146, - 9, 9, 9, 11, 11, 11, 251, 252, 13, 13, 13, 13, 13, 13, 253, 254, - 11, 11, 11, 47, 47, 47, 255, 256, 47, 47, 47, 47, 47, 47, 32, 32, - 257, 258, 259, 260, 261, 262, 263, 263, 264, 265, 266, 267, 268, 47, 47, 47, - 47, 269, 148, 47, 47, 47, 47, 270, 47, 271, 47, 47, 146, 146, 146, 47, - 146, 146, 272, 146, 273, 274, 146, 146, 272, 146, 146, 274, 146, 146, 146, 146, - 47, 47, 47, 47, 146, 146, 146, 146, 47, 275, 47, 47, 47, 47, 47, 47, - 47, 146, 146, 146, 146, 47, 47, 187, 276, 47, 61, 47, 13, 13, 277, 278, - 13, 279, 47, 47, 47, 47, 280, 281, 31, 282, 283, 284, 13, 13, 13, 285, - 286, 287, 288, 289, 290, 291, 9, 292, 293, 47, 294, 295, 47, 47, 47, 296, - 297, 47, 47, 298, 299, 160, 32, 300, 61, 47, 301, 47, 302, 303, 47, 47, - 72, 47, 47, 304, 305, 306, 307, 61, 47, 47, 308, 309, 310, 311, 47, 312, - 47, 47, 47, 313, 58, 314, 315, 316, 47, 47, 47, 11, 11, 317, 318, 11, - 11, 11, 11, 11, 47, 47, 319, 160, 320, 320, 320, 320, 320, 320, 320, 320, - 321, 321, 321, 321, 321, 321, 321, 321, 11, 322, 323, 47, 47, 47, 47, 47, - 47, 47, 47, 324, 31, 325, 47, 47, 47, 47, 47, 326, 146, 47, 47, 47, - 47, 47, 47, 47, 327, 146, 146, 328, 32, 329, 32, 330, 331, 332, 333, 47, - 47, 47, 47, 47, 47, 47, 47, 334, 335, 2, 3, 4, 5, 336, 337, 338, - 47, 339, 47, 47, 47, 47, 340, 341, 342, 145, 145, 343, 220, 220, 220, 344, - 345, 146, 146, 146, 146, 146, 146, 346, 347, 347, 347, 347, 347, 347, 347, 347, - 47, 47, 47, 47, 47, 47, 348, 145, 47, 47, 349, 47, 350, 47, 47, 60, - 47, 351, 47, 47, 47, 352, 220, 220, 9, 9, 147, 11, 11, 47, 47, 47, - 47, 47, 160, 9, 9, 147, 11, 11, 47, 47, 47, 47, 47, 47, 351, 9, - 9, 353, 11, 11, 47, 47, 47, 47, 27, 27, 27, 27, 27, 27, 27, 27, - 47, 47, 47, 47, 47, 354, 47, 355, 47, 47, 356, 145, 145, 145, 47, 357, - 47, 358, 47, 351, 66, 66, 66, 66, 47, 47, 47, 359, 145, 145, 145, 145, - 360, 47, 47, 361, 145, 66, 47, 362, 47, 363, 145, 145, 364, 47, 365, 66, - 47, 47, 47, 366, 47, 367, 47, 367, 47, 366, 144, 145, 145, 145, 145, 145, - 9, 9, 9, 9, 11, 11, 11, 368, 47, 47, 369, 160, 370, 9, 371, 11, - 372, 227, 227, 227, 227, 227, 227, 227, 145, 145, 145, 145, 145, 145, 145, 145, - 47, 47, 373, 47, 47, 47, 47, 374, 47, 363, 375, 47, 60, 376, 66, 47, - 377, 66, 66, 47, 378, 145, 47, 47, 379, 47, 47, 361, 380, 381, 382, 383, - 180, 47, 47, 384, 385, 47, 47, 160, 97, 47, 386, 387, 388, 47, 47, 389, - 180, 47, 47, 390, 391, 392, 393, 145, 47, 47, 394, 395, 360, 32, 32, 32, - 47, 47, 366, 47, 47, 396, 172, 160, 92, 47, 47, 113, 397, 398, 399, 32, - 47, 47, 47, 400, 401, 402, 403, 32, 47, 47, 47, 404, 405, 406, 47, 47, - 47, 47, 47, 407, 408, 160, 160, 160, 47, 47, 409, 410, 411, 412, 32, 32, - 47, 47, 47, 413, 414, 160, 66, 66, 47, 47, 415, 416, 160, 160, 160, 160, - 47, 417, 418, 419, 47, 47, 47, 47, 47, 47, 394, 420, 66, 66, 66, 66, - 9, 9, 9, 9, 11, 11, 128, 421, 47, 47, 47, 422, 423, 160, 160, 160, - 47, 47, 47, 47, 47, 424, 425, 426, 427, 47, 47, 428, 429, 430, 47, 47, - 431, 432, 66, 47, 47, 47, 47, 47, 66, 66, 66, 66, 66, 66, 66, 66, - 47, 47, 47, 47, 47, 47, 433, 160, 47, 47, 409, 434, 433, 128, 145, 435, - 47, 156, 436, 437, 32, 32, 32, 32, 47, 47, 47, 360, 438, 160, 47, 47, - 439, 440, 160, 160, 160, 160, 160, 160, 47, 47, 47, 47, 47, 47, 47, 441, - 442, 47, 47, 443, 444, 445, 32, 32, 47, 47, 47, 47, 145, 446, 447, 448, - 220, 220, 220, 220, 220, 220, 220, 66, 47, 47, 47, 47, 47, 47, 47, 433, - 47, 47, 47, 209, 449, 32, 47, 47, 47, 450, 451, 160, 160, 160, 160, 160, - 47, 47, 47, 47, 47, 47, 306, 47, 47, 47, 47, 47, 160, 47, 47, 452, - 47, 47, 47, 453, 454, 455, 456, 47, 27, 27, 27, 27, 457, 47, 458, 160, - 9, 9, 9, 9, 9, 9, 11, 11, 145, 459, 66, 66, 66, 66, 66, 66, - 47, 47, 47, 47, 396, 460, 426, 426, 461, 462, 27, 27, 27, 27, 463, 426, - 47, 464, 209, 209, 209, 209, 209, 209, 146, 146, 146, 146, 146, 146, 146, 160, - 32, 32, 32, 32, 32, 146, 146, 146, 146, 146, 146, 146, 146, 146, 465, 466, - 467, 146, 468, 146, 146, 146, 146, 146, 146, 146, 146, 146, 469, 146, 146, 146, - 9, 470, 11, 471, 472, 11, 197, 9, 473, 474, 9, 475, 11, 9, 470, 11, - 471, 472, 11, 197, 9, 473, 474, 9, 475, 11, 9, 470, 11, 471, 472, 11, - 197, 9, 473, 474, 9, 475, 11, 9, 470, 11, 197, 9, 476, 477, 478, 479, - 11, 480, 9, 481, 482, 483, 484, 11, 485, 9, 486, 11, 487, 160, 160, 160, - 32, 32, 32, 488, 32, 32, 489, 490, 491, 492, 32, 32, 32, 32, 32, 32, - 493, 11, 11, 11, 11, 11, 11, 11, 32, 32, 32, 27, 27, 27, 27, 27, - 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 47, 494, 495, 146, 146, 146, - 47, 47, 450, 32, 47, 47, 374, 496, 47, 47, 47, 47, 47, 47, 497, 160, - 47, 47, 47, 47, 47, 47, 450, 498, 47, 47, 47, 47, 356, 32, 32, 32, - 9, 9, 473, 11, 499, 306, 66, 66, 145, 145, 500, 501, 145, 145, 145, 145, - 145, 145, 502, 145, 145, 145, 145, 145, 47, 47, 47, 47, 47, 47, 47, 227, - 503, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 504, - 209, 209, 209, 209, 209, 209, 209, 209, 0, 0, 0, 0, 0, 0, 0, 0, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 716, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 717, + 0, 0, 0, 0, 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 0, 0, 7, 0, + 8, 8, 8, 8, 8, 8, 8, 9, 10, 11, 12, 11, 11, 11, 13, 11, + 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 16, 17, 18, 17, 17, 19, 20, 21, 21, 22, 21, 23, 24, + 25, 26, 27, 27, 28, 29, 27, 30, 27, 27, 27, 27, 27, 31, 27, 27, + 32, 33, 33, 33, 34, 27, 27, 27, 35, 35, 35, 36, 37, 37, 37, 38, + 39, 39, 40, 41, 42, 43, 44, 27, 27, 45, 27, 27, 27, 27, 46, 27, + 47, 47, 47, 47, 47, 48, 49, 47, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 108, 109, 110, 111, 108, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 121, 122, 121, 123, 124, 124, 125, 126, 127, 128, 129, 130, 124, 124, + 131, 131, 131, 131, 132, 131, 133, 134, 131, 132, 131, 135, 135, 136, 124, 124, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 138, 138, 139, 138, 138, 140, + 141, 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, 142, 143, 144, 142, 142, + 143, 142, 142, 145, 146, 147, 142, 142, 142, 146, 142, 142, 142, 148, 142, 149, + 142, 150, 151, 151, 151, 151, 151, 152, 153, 153, 153, 153, 153, 153, 153, 153, + 154, 155, 156, 156, 156, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 167, 167, 167, 167, 168, 169, 169, 170, 171, 172, 172, 172, 172, 172, 173, + 172, 172, 174, 153, 153, 153, 153, 175, 176, 177, 178, 178, 179, 180, 181, 182, + 183, 183, 184, 183, 185, 186, 167, 167, 187, 188, 189, 189, 189, 190, 189, 191, + 192, 192, 193, 8, 8, 194, 195, 124, 196, 196, 196, 196, 197, 196, 196, 196, + 198, 198, 198, 198, 199, 199, 199, 200, 201, 201, 201, 202, 203, 204, 204, 204, + 205, 138, 138, 206, 207, 208, 209, 210, 4, 4, 211, 4, 4, 212, 213, 214, + 4, 4, 4, 215, 8, 8, 8, 8, 11, 216, 11, 11, 216, 217, 11, 218, + 11, 11, 11, 219, 219, 220, 11, 221, 222, 0, 0, 0, 0, 0, 223, 224, + 225, 226, 0, 0, 227, 8, 8, 228, 0, 0, 229, 230, 231, 0, 4, 4, + 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 233, 124, 234, 124, 0, 0, 235, 235, 235, 235, 235, 235, 235, 235, + 0, 0, 0, 0, 0, 0, 0, 236, 237, 237, 237, 237, 237, 237, 4, 4, + 238, 238, 238, 238, 238, 238, 238, 239, 138, 138, 139, 240, 240, 240, 241, 242, + 142, 243, 244, 244, 244, 244, 14, 14, 0, 0, 0, 0, 0, 245, 124, 124, + 246, 247, 246, 246, 246, 246, 246, 248, 246, 246, 246, 246, 246, 246, 246, 246, + 246, 246, 246, 246, 246, 249, 124, 0, 250, 0, 251, 252, 253, 254, 254, 254, + 254, 255, 256, 257, 257, 257, 257, 258, 259, 260, 260, 261, 141, 141, 141, 141, + 262, 0, 260, 260, 0, 0, 263, 257, 141, 262, 0, 0, 0, 0, 141, 264, + 0, 0, 0, 0, 0, 257, 257, 265, 257, 257, 257, 257, 257, 266, 0, 0, + 246, 246, 246, 246, 0, 0, 0, 0, 267, 267, 267, 267, 267, 267, 267, 267, + 268, 267, 267, 267, 269, 270, 270, 270, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 272, 124, 14, 14, 14, 14, 14, 14, 273, 273, 273, 273, 273, 274, + 0, 0, 275, 4, 4, 4, 4, 4, 276, 4, 4, 4, 4, 226, 124, 277, + 278, 278, 279, 233, 280, 280, 280, 281, 282, 282, 282, 282, 283, 284, 47, 47, + 285, 285, 286, 287, 287, 288, 141, 289, 290, 290, 290, 290, 291, 292, 137, 293, + 294, 294, 294, 295, 296, 297, 137, 137, 298, 298, 298, 298, 299, 300, 301, 302, + 303, 304, 244, 4, 4, 305, 306, 151, 151, 151, 151, 151, 301, 301, 307, 308, + 141, 141, 309, 141, 310, 141, 141, 311, 124, 124, 124, 124, 124, 124, 124, 124, + 246, 246, 246, 246, 246, 246, 312, 246, 246, 246, 246, 246, 246, 313, 124, 124, + 314, 315, 21, 316, 317, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 318, 27, 27, 27, 27, 27, 27, 27, 27, 27, 124, 124, 27, + 8, 233, 319, 0, 0, 320, 321, 322, 27, 27, 27, 27, 27, 27, 27, 323, + 324, 0, 1, 2, 1, 2, 325, 256, 257, 326, 141, 262, 327, 328, 329, 330, + 331, 332, 333, 334, 335, 335, 124, 124, 332, 332, 332, 332, 332, 332, 332, 336, + 337, 0, 0, 338, 11, 11, 11, 11, 339, 340, 341, 124, 124, 0, 0, 342, + 343, 344, 345, 345, 345, 346, 347, 348, 349, 349, 350, 351, 352, 353, 353, 354, + 355, 356, 357, 357, 358, 359, 124, 124, 360, 360, 360, 360, 360, 361, 361, 361, + 362, 363, 364, 365, 365, 366, 365, 367, 368, 368, 369, 370, 370, 370, 371, 372, + 372, 373, 374, 375, 376, 376, 376, 377, 378, 378, 378, 378, 378, 378, 378, 378, + 378, 378, 378, 379, 378, 380, 381, 124, 382, 4, 4, 383, 124, 124, 124, 124, + 384, 385, 385, 386, 387, 388, 389, 389, 390, 391, 392, 124, 124, 124, 393, 394, + 395, 396, 397, 398, 399, 400, 124, 124, 401, 401, 402, 403, 402, 404, 402, 402, + 405, 406, 407, 408, 409, 409, 410, 410, 411, 411, 124, 124, 412, 412, 413, 414, + 415, 415, 415, 416, 417, 418, 419, 420, 421, 422, 423, 124, 124, 124, 124, 124, + 424, 424, 424, 424, 425, 124, 124, 124, 426, 426, 426, 427, 426, 426, 426, 428, + 429, 429, 430, 431, 432, 432, 433, 432, 434, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 27, 435, 436, 436, 437, 438, 439, 440, 124, 441, + 442, 442, 443, 444, 444, 445, 124, 446, 447, 124, 124, 448, 449, 124, 450, 451, + 452, 452, 452, 452, 453, 454, 452, 455, 456, 456, 456, 456, 457, 458, 459, 460, + 461, 461, 461, 462, 463, 464, 464, 465, 466, 466, 466, 466, 466, 466, 467, 468, + 469, 470, 469, 469, 471, 124, 124, 124, 472, 473, 474, 475, 475, 475, 476, 477, + 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 487, 488, 489, 490, 491, 124, + 492, 492, 492, 492, 492, 493, 494, 124, 495, 495, 495, 495, 496, 497, 124, 124, + 498, 498, 498, 499, 498, 500, 124, 124, 501, 501, 501, 501, 502, 503, 504, 124, + 505, 505, 505, 506, 506, 137, 507, 124, 508, 509, 510, 508, 511, 124, 124, 124, + 512, 512, 512, 513, 124, 124, 124, 124, 124, 124, 514, 514, 514, 514, 514, 515, + 516, 517, 518, 519, 520, 521, 124, 124, 124, 124, 522, 523, 523, 522, 524, 124, + 525, 525, 525, 525, 526, 527, 527, 527, 527, 527, 528, 153, 529, 529, 529, 530, + 531, 124, 124, 124, 124, 124, 532, 124, 124, 124, 124, 124, 533, 533, 534, 535, + 536, 537, 537, 538, 539, 537, 540, 541, 541, 542, 543, 544, 124, 124, 124, 124, + 545, 546, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 555, 556, 557, 124, + 124, 124, 124, 124, 124, 124, 558, 559, 560, 561, 560, 562, 560, 563, 124, 124, + 124, 124, 124, 564, 565, 565, 565, 566, 567, 567, 567, 567, 567, 567, 567, 567, + 567, 568, 124, 124, 124, 124, 124, 124, 567, 567, 567, 567, 567, 567, 569, 570, + 567, 567, 567, 567, 571, 124, 124, 124, 124, 572, 572, 572, 572, 572, 572, 573, + 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 575, 574, 574, + 574, 574, 574, 574, 574, 574, 574, 576, 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 578, 124, 124, 124, 579, 579, 579, 580, 124, 124, 124, 124, + 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, 581, 582, 583, 584, 585, + 585, 585, 585, 586, 587, 588, 589, 590, 591, 591, 591, 591, 592, 593, 594, 595, + 591, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 596, 596, 596, 597, + 124, 124, 124, 124, 598, 598, 598, 598, 598, 599, 600, 601, 600, 602, 124, 124, + 603, 603, 603, 603, 604, 603, 603, 603, 605, 603, 124, 124, 124, 124, 606, 607, + 608, 608, 608, 608, 608, 608, 608, 608, 609, 609, 609, 609, 609, 609, 609, 609, + 609, 609, 609, 609, 609, 610, 124, 611, 608, 612, 124, 124, 124, 124, 124, 124, + 608, 608, 608, 608, 608, 608, 608, 613, 124, 124, 124, 124, 124, 124, 124, 614, + 615, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 616, 617, 124, 618, 619, 620, 620, 620, 620, 620, 620, 620, 620, 620, + 620, 620, 620, 620, 620, 620, 620, 621, 622, 622, 622, 622, 622, 622, 623, 624, + 625, 626, 627, 124, 124, 124, 124, 124, 0, 0, 0, 0, 0, 0, 0, 340, + 0, 0, 0, 628, 0, 629, 0, 629, 8, 8, 194, 8, 630, 0, 0, 0, + 0, 0, 0, 0, 627, 124, 124, 124, 0, 0, 0, 0, 0, 0, 0, 631, + 0, 0, 632, 0, 0, 0, 633, 634, 635, 0, 636, 0, 0, 0, 234, 124, + 11, 11, 11, 11, 637, 124, 124, 124, 124, 124, 124, 124, 0, 627, 0, 627, + 0, 0, 0, 0, 0, 638, 0, 639, 0, 0, 0, 0, 0, 223, 0, 0, + 0, 640, 641, 642, 643, 0, 0, 0, 644, 645, 0, 646, 647, 648, 0, 0, + 0, 0, 649, 0, 0, 0, 0, 0, 0, 0, 0, 0, 650, 0, 0, 0, + 651, 651, 651, 651, 651, 651, 651, 651, 652, 653, 654, 124, 124, 124, 124, 124, + 4, 655, 656, 124, 124, 124, 124, 124, 657, 658, 659, 14, 14, 14, 660, 124, + 661, 124, 124, 124, 124, 124, 124, 124, 662, 662, 663, 664, 665, 124, 124, 124, + 124, 666, 667, 124, 668, 668, 668, 669, 124, 124, 124, 124, 124, 670, 670, 671, + 124, 124, 124, 124, 124, 672, 672, 673, 124, 124, 124, 124, 674, 675, 674, 676, + 124, 124, 124, 124, 124, 124, 677, 678, 679, 679, 679, 679, 679, 679, 679, 679, + 679, 679, 679, 679, 680, 681, 124, 124, 682, 682, 682, 682, 683, 684, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 324, 0, 0, 0, 685, 124, 124, 124, 124, + 324, 0, 0, 245, 124, 124, 124, 124, 686, 27, 687, 688, 689, 690, 691, 692, + 693, 694, 695, 694, 124, 124, 124, 696, 0, 0, 348, 0, 0, 0, 0, 0, + 0, 627, 225, 324, 324, 324, 0, 631, 0, 0, 245, 124, 124, 124, 697, 0, + 698, 0, 0, 348, 639, 227, 631, 124, 0, 0, 0, 0, 0, 699, 340, 340, + 0, 0, 0, 0, 0, 233, 348, 629, 348, 0, 0, 0, 700, 233, 0, 0, + 700, 0, 245, 348, 227, 639, 124, 124, 0, 0, 0, 0, 0, 700, 245, 340, + 701, 0, 0, 0, 702, 703, 704, 639, 0, 320, 0, 0, 0, 0, 0, 234, + 246, 246, 246, 246, 246, 246, 124, 124, 246, 312, 246, 246, 246, 246, 246, 246, + 246, 246, 312, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 705, 246, + 246, 246, 246, 246, 246, 312, 124, 124, 246, 312, 124, 124, 124, 124, 124, 124, + 246, 246, 246, 246, 706, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 313, + 707, 124, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 1, 2, 2, 2, + 2, 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, 2, 2, + 2, 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 8, 8, 8, 8, 16, 8, 8, 8, 17, 18, 18, 18, + 19, 19, 19, 19, 19, 20, 19, 19, 21, 22, 22, 22, 22, 22, 22, 22, + 22, 23, 21, 22, 22, 22, 23, 21, 24, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 12, 12, 25, 25, 26, 27, 25, 28, 12, 12, 29, 30, 29, 31, + 29, 29, 32, 32, 29, 29, 29, 29, 31, 29, 33, 7, 7, 34, 29, 29, + 35, 29, 29, 29, 29, 29, 29, 30, 36, 36, 36, 37, 36, 36, 36, 36, + 36, 36, 38, 39, 40, 40, 40, 40, 41, 12, 12, 12, 42, 42, 42, 42, + 42, 42, 43, 44, 45, 45, 45, 45, 45, 45, 45, 46, 45, 45, 45, 47, + 48, 48, 48, 48, 48, 48, 48, 49, 36, 36, 38, 12, 50, 51, 29, 29, + 52, 29, 29, 29, 53, 53, 53, 53, 54, 55, 53, 53, 53, 56, 53, 53, + 57, 58, 57, 59, 59, 57, 57, 57, 57, 57, 60, 57, 61, 62, 63, 57, + 57, 59, 59, 64, 12, 65, 12, 66, 57, 62, 57, 57, 57, 57, 57, 64, + 67, 67, 68, 69, 70, 71, 71, 71, 71, 71, 72, 71, 72, 73, 74, 72, + 68, 69, 70, 74, 75, 12, 67, 76, 12, 77, 71, 71, 71, 68, 12, 12, + 78, 78, 79, 80, 80, 79, 79, 79, 79, 79, 81, 79, 81, 78, 82, 79, + 79, 80, 80, 82, 83, 12, 12, 12, 79, 84, 79, 79, 82, 12, 78, 79, + 85, 85, 86, 87, 87, 86, 86, 86, 86, 86, 88, 86, 88, 85, 89, 86, + 86, 87, 87, 89, 12, 85, 12, 90, 86, 91, 86, 86, 86, 86, 12, 12, + 92, 93, 94, 92, 95, 96, 97, 95, 98, 99, 94, 92, 100, 100, 96, 92, + 94, 92, 95, 96, 99, 98, 12, 12, 12, 92, 100, 100, 100, 100, 94, 12, + 101, 101, 101, 102, 102, 101, 101, 101, 101, 101, 102, 101, 101, 101, 103, 101, + 101, 102, 102, 103, 12, 104, 105, 103, 101, 106, 101, 101, 12, 107, 101, 101, + 108, 108, 108, 109, 109, 108, 108, 108, 108, 108, 109, 108, 108, 110, 111, 108, + 108, 109, 109, 111, 12, 112, 12, 113, 108, 114, 108, 108, 110, 12, 12, 12, + 115, 115, 115, 116, 116, 115, 115, 115, 115, 115, 115, 115, 115, 116, 116, 115, + 12, 115, 115, 115, 115, 117, 115, 115, 118, 118, 119, 119, 119, 120, 121, 119, + 119, 119, 119, 119, 122, 119, 119, 123, 119, 120, 124, 125, 119, 126, 119, 119, + 12, 121, 119, 119, 121, 127, 12, 12, 128, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 130, 131, 129, 129, 129, 12, 12, 12, 12, 12, 132, 133, 134, 135, + 135, 135, 135, 135, 135, 136, 135, 135, 135, 135, 135, 137, 135, 138, 135, 134, + 135, 135, 137, 135, 139, 139, 139, 139, 139, 139, 140, 139, 139, 139, 139, 141, + 140, 139, 139, 139, 139, 139, 139, 142, 139, 143, 144, 12, 145, 145, 145, 145, + 146, 146, 146, 146, 146, 147, 12, 148, 146, 146, 149, 146, 150, 150, 150, 150, + 151, 151, 151, 151, 151, 151, 152, 153, 151, 154, 152, 153, 152, 153, 151, 154, + 152, 153, 151, 151, 151, 154, 151, 151, 151, 151, 154, 155, 151, 151, 151, 156, + 151, 151, 153, 12, 157, 157, 157, 157, 157, 158, 157, 158, 159, 159, 159, 159, + 160, 160, 160, 160, 160, 160, 160, 161, 162, 162, 162, 162, 162, 162, 163, 164, + 162, 162, 165, 12, 166, 166, 166, 166, 166, 167, 12, 168, 169, 169, 169, 169, + 169, 170, 12, 12, 171, 171, 171, 171, 171, 12, 12, 12, 172, 172, 172, 173, + 173, 12, 12, 12, 174, 174, 174, 174, 174, 174, 174, 175, 174, 174, 175, 12, + 176, 177, 178, 178, 178, 178, 179, 12, 178, 178, 178, 178, 178, 178, 180, 12, + 178, 178, 181, 12, 159, 182, 12, 12, 183, 183, 183, 183, 183, 183, 183, 184, + 183, 183, 183, 12, 185, 183, 183, 183, 186, 186, 186, 186, 186, 186, 186, 187, + 186, 188, 12, 12, 189, 189, 189, 189, 189, 189, 189, 12, 189, 189, 190, 12, + 189, 189, 191, 192, 193, 193, 193, 193, 193, 193, 193, 194, 195, 195, 195, 195, + 195, 195, 195, 196, 195, 195, 195, 197, 195, 195, 198, 12, 195, 195, 195, 198, + 7, 7, 7, 199, 7, 7, 7, 12, 200, 200, 200, 200, 200, 200, 200, 201, + 202, 202, 202, 202, 203, 203, 203, 203, 203, 12, 12, 203, 204, 204, 204, 204, + 204, 204, 205, 204, 204, 204, 206, 207, 208, 208, 208, 208, 19, 19, 209, 12, + 146, 146, 210, 211, 202, 202, 12, 12, 212, 7, 7, 7, 213, 7, 214, 215, + 0, 214, 216, 12, 2, 217, 218, 2, 2, 2, 2, 219, 220, 217, 221, 2, + 2, 2, 222, 2, 2, 2, 2, 223, 8, 224, 8, 224, 8, 8, 225, 225, + 8, 8, 8, 224, 8, 15, 8, 8, 8, 10, 8, 226, 10, 15, 8, 14, + 0, 0, 0, 227, 0, 228, 0, 0, 229, 0, 0, 230, 0, 0, 0, 231, + 2, 2, 2, 232, 233, 12, 12, 12, 234, 12, 12, 12, 0, 235, 236, 0, + 4, 0, 0, 0, 0, 0, 0, 4, 2, 2, 5, 12, 0, 0, 233, 12, + 0, 0, 231, 12, 237, 237, 237, 237, 0, 238, 0, 0, 239, 239, 239, 239, + 18, 18, 18, 18, 18, 12, 240, 18, 241, 241, 241, 241, 241, 241, 12, 242, + 243, 12, 12, 242, 151, 154, 12, 12, 151, 154, 151, 154, 0, 0, 0, 233, + 244, 244, 244, 244, 244, 244, 245, 244, 244, 12, 12, 12, 244, 246, 12, 12, + 0, 247, 0, 0, 248, 244, 249, 250, 0, 0, 244, 0, 251, 252, 252, 252, + 252, 252, 252, 252, 252, 253, 254, 255, 256, 257, 257, 257, 257, 257, 257, 257, + 257, 257, 258, 256, 12, 259, 260, 260, 260, 260, 260, 260, 261, 150, 150, 150, + 150, 150, 150, 262, 0, 233, 12, 131, 150, 150, 150, 263, 257, 257, 257, 258, + 257, 257, 0, 0, 264, 264, 264, 264, 264, 264, 264, 265, 264, 266, 12, 12, + 267, 267, 267, 267, 268, 268, 268, 268, 268, 268, 268, 12, 269, 269, 269, 269, + 269, 269, 12, 12, 236, 2, 2, 2, 2, 2, 230, 2, 270, 2, 2, 2, + 271, 271, 271, 271, 271, 271, 271, 272, 273, 273, 273, 273, 273, 273, 12, 12, + 274, 274, 274, 274, 274, 275, 12, 276, 274, 274, 275, 12, 277, 277, 277, 277, + 277, 277, 277, 278, 279, 279, 279, 279, 279, 12, 12, 280, 150, 150, 150, 281, + 282, 282, 282, 282, 282, 282, 282, 283, 282, 282, 284, 285, 145, 145, 145, 286, + 287, 287, 287, 287, 287, 288, 12, 12, 287, 287, 287, 289, 287, 287, 289, 287, + 290, 290, 290, 290, 291, 12, 12, 12, 12, 12, 292, 290, 293, 293, 293, 293, + 293, 294, 12, 12, 155, 154, 155, 154, 155, 154, 12, 12, 2, 2, 3, 2, + 2, 295, 296, 12, 293, 293, 293, 297, 293, 293, 297, 12, 150, 12, 12, 12, + 150, 262, 298, 150, 150, 150, 150, 12, 244, 244, 244, 246, 244, 244, 246, 12, + 2, 299, 12, 12, 300, 22, 12, 24, 25, 26, 25, 301, 302, 303, 25, 25, + 29, 29, 29, 304, 7, 7, 7, 305, 231, 0, 0, 0, 0, 231, 0, 12, + 29, 306, 29, 29, 29, 29, 29, 307, 308, 0, 0, 0, 0, 309, 257, 257, + 257, 257, 257, 310, 311, 150, 311, 150, 311, 150, 311, 281, 0, 231, 0, 231, + 12, 12, 308, 233, 312, 312, 312, 313, 312, 312, 312, 312, 312, 314, 312, 312, + 312, 312, 314, 315, 312, 312, 312, 316, 312, 312, 314, 12, 231, 131, 0, 0, + 0, 131, 0, 0, 8, 8, 8, 14, 0, 0, 0, 317, 318, 12, 12, 12, + 0, 0, 0, 319, 320, 320, 320, 320, 320, 320, 320, 321, 322, 322, 322, 322, + 323, 12, 12, 12, 214, 0, 0, 0, 0, 0, 0, 12, 324, 324, 324, 324, + 324, 12, 12, 325, 326, 326, 326, 326, 326, 326, 327, 12, 328, 328, 328, 328, + 328, 328, 329, 12, 330, 330, 330, 330, 330, 330, 330, 331, 332, 332, 332, 332, + 332, 12, 332, 332, 332, 333, 12, 12, 334, 334, 334, 334, 335, 335, 335, 335, + 336, 336, 336, 336, 336, 336, 336, 337, 336, 336, 337, 12, 338, 338, 338, 338, + 338, 12, 338, 338, 338, 338, 338, 12, 339, 339, 339, 339, 339, 339, 12, 12, + 340, 340, 340, 340, 340, 12, 12, 341, 342, 342, 343, 342, 343, 344, 342, 342, + 344, 342, 342, 342, 344, 342, 344, 345, 346, 346, 346, 346, 346, 12, 12, 12, + 347, 347, 347, 347, 347, 348, 12, 12, 347, 349, 12, 12, 347, 347, 12, 12, + 2, 350, 2, 2, 351, 2, 299, 12, 352, 353, 354, 352, 352, 352, 352, 352, + 352, 355, 356, 357, 358, 358, 358, 358, 358, 359, 358, 358, 360, 360, 360, 360, + 361, 361, 361, 361, 361, 361, 361, 362, 12, 363, 361, 361, 364, 364, 364, 364, + 365, 366, 367, 364, 368, 368, 368, 368, 368, 368, 368, 369, 370, 370, 370, 370, + 370, 370, 371, 372, 373, 373, 373, 373, 373, 373, 374, 12, 375, 375, 375, 375, + 376, 376, 376, 376, 376, 376, 12, 376, 377, 376, 376, 376, 378, 379, 12, 378, + 378, 380, 380, 378, 378, 378, 378, 378, 378, 381, 382, 383, 378, 378, 384, 12, + 385, 385, 385, 385, 386, 386, 386, 386, 387, 387, 387, 387, 387, 388, 389, 387, + 387, 388, 12, 12, 390, 390, 390, 390, 390, 391, 392, 390, 393, 393, 393, 393, + 393, 394, 393, 393, 395, 395, 395, 395, 396, 12, 395, 395, 397, 397, 397, 397, + 398, 12, 399, 400, 12, 12, 399, 397, 401, 401, 401, 401, 401, 401, 402, 12, + 403, 403, 403, 403, 404, 12, 12, 12, 404, 12, 405, 403, 406, 406, 406, 406, + 406, 406, 12, 12, 406, 406, 407, 12, 408, 408, 408, 408, 408, 409, 410, 408, + 408, 409, 12, 411, 29, 29, 29, 412, 413, 413, 413, 413, 413, 413, 414, 415, + 415, 12, 12, 12, 416, 29, 12, 12, 29, 29, 417, 12, 12, 12, 416, 29, + 418, 418, 418, 418, 418, 418, 12, 12, 419, 419, 419, 419, 419, 419, 420, 12, + 421, 421, 421, 421, 421, 421, 422, 12, 423, 423, 423, 423, 423, 423, 423, 12, + 424, 424, 424, 424, 424, 425, 12, 12, 426, 426, 426, 426, 426, 426, 426, 427, + 428, 426, 426, 426, 426, 427, 12, 429, 430, 430, 430, 430, 431, 12, 12, 432, + 433, 433, 433, 433, 433, 433, 434, 12, 433, 433, 435, 12, 436, 436, 436, 436, + 436, 437, 436, 436, 436, 436, 12, 12, 438, 438, 438, 438, 438, 439, 12, 12, + 440, 440, 440, 440, 118, 119, 119, 119, 119, 127, 12, 12, 441, 441, 441, 441, + 442, 441, 441, 441, 443, 12, 12, 12, 444, 445, 446, 447, 444, 444, 444, 447, + 444, 444, 448, 12, 449, 449, 449, 449, 449, 449, 450, 12, 449, 449, 451, 12, + 452, 453, 452, 454, 454, 452, 452, 452, 452, 452, 455, 452, 455, 453, 456, 452, + 452, 454, 454, 457, 458, 459, 12, 453, 452, 460, 452, 458, 452, 458, 12, 12, + 461, 461, 462, 463, 461, 461, 461, 461, 461, 462, 461, 461, 464, 465, 466, 461, + 461, 462, 467, 12, 468, 12, 12, 12, 469, 469, 469, 469, 469, 469, 469, 470, + 471, 12, 12, 12, 472, 472, 472, 472, 472, 472, 12, 12, 472, 472, 473, 12, + 474, 474, 474, 474, 474, 475, 474, 474, 474, 474, 474, 475, 476, 476, 476, 476, + 476, 477, 12, 12, 476, 476, 478, 12, 178, 178, 178, 180, 479, 479, 479, 479, + 479, 479, 480, 12, 145, 12, 12, 12, 481, 481, 481, 481, 481, 481, 482, 483, + 481, 481, 481, 12, 481, 482, 12, 12, 484, 484, 484, 484, 484, 484, 484, 12, + 485, 485, 485, 485, 486, 12, 12, 487, 488, 489, 490, 488, 488, 491, 488, 488, + 488, 488, 488, 488, 488, 492, 493, 488, 488, 489, 12, 12, 488, 488, 494, 12, + 495, 495, 496, 495, 495, 495, 495, 495, 495, 497, 12, 12, 498, 498, 498, 498, + 498, 498, 12, 12, 499, 499, 499, 499, 500, 12, 12, 12, 501, 501, 501, 501, + 501, 501, 502, 12, 53, 53, 503, 12, 440, 440, 12, 12, 504, 504, 504, 504, + 505, 12, 12, 12, 504, 504, 505, 12, 506, 506, 507, 506, 506, 506, 506, 506, + 506, 508, 506, 506, 506, 509, 12, 12, 506, 506, 506, 510, 511, 511, 511, 511, + 512, 511, 511, 511, 511, 511, 513, 511, 511, 514, 12, 12, 515, 516, 517, 515, + 515, 515, 515, 515, 515, 516, 518, 517, 515, 515, 12, 12, 515, 515, 519, 12, + 520, 521, 522, 520, 520, 520, 520, 520, 520, 520, 520, 523, 521, 520, 524, 12, + 520, 520, 525, 12, 526, 526, 526, 526, 526, 526, 526, 12, 526, 526, 527, 12, + 528, 528, 528, 528, 528, 528, 529, 12, 530, 530, 530, 530, 531, 530, 530, 530, + 530, 530, 532, 533, 530, 530, 532, 12, 534, 12, 12, 12, 100, 100, 100, 100, + 96, 12, 12, 98, 535, 535, 535, 535, 535, 535, 536, 12, 535, 535, 535, 537, + 535, 538, 12, 12, 535, 12, 12, 12, 539, 539, 539, 539, 540, 12, 12, 12, + 541, 541, 541, 541, 541, 542, 12, 12, 541, 541, 543, 12, 544, 544, 544, 544, + 544, 545, 12, 12, 546, 546, 546, 546, 546, 546, 547, 12, 269, 269, 548, 12, + 549, 549, 549, 549, 549, 549, 549, 550, 549, 549, 551, 552, 553, 553, 553, 553, + 553, 553, 553, 554, 553, 553, 555, 12, 556, 556, 556, 556, 556, 556, 556, 557, + 556, 557, 12, 12, 558, 558, 558, 558, 558, 559, 12, 12, 558, 558, 560, 558, + 560, 558, 558, 558, 558, 558, 12, 561, 562, 562, 562, 562, 562, 562, 563, 12, + 564, 564, 564, 564, 564, 564, 565, 12, 566, 566, 566, 566, 566, 566, 567, 566, + 566, 12, 12, 12, 568, 568, 568, 568, 568, 568, 569, 570, 568, 568, 12, 570, + 571, 572, 12, 12, 244, 573, 12, 12, 574, 574, 574, 574, 575, 575, 575, 575, + 575, 576, 12, 12, 12, 12, 12, 577, 574, 574, 574, 578, 578, 12, 12, 12, + 257, 579, 257, 580, 581, 252, 252, 252, 582, 12, 12, 12, 583, 12, 12, 12, + 253, 584, 12, 12, 12, 257, 12, 12, 585, 585, 585, 585, 585, 585, 585, 12, + 586, 586, 586, 586, 586, 586, 587, 12, 586, 586, 586, 588, 586, 586, 588, 12, + 586, 586, 589, 586, 0, 12, 12, 12, 0, 12, 238, 0, 317, 12, 12, 12, + 7, 590, 12, 12, 0, 233, 12, 12, 0, 231, 308, 0, 0, 591, 227, 0, + 0, 0, 591, 7, 212, 592, 7, 0, 0, 0, 593, 227, 8, 224, 12, 12, + 0, 231, 12, 12, 0, 0, 317, 12, 0, 0, 0, 228, 594, 595, 308, 228, + 0, 0, 596, 308, 0, 308, 0, 0, 0, 596, 231, 308, 0, 228, 0, 228, + 0, 0, 596, 231, 0, 597, 238, 0, 228, 0, 0, 0, 0, 233, 0, 0, + 0, 0, 0, 238, 598, 598, 598, 598, 598, 598, 598, 12, 12, 12, 599, 598, + 600, 598, 598, 598, 2, 2, 2, 299, 12, 270, 299, 12, 239, 601, 239, 239, + 239, 239, 602, 239, 603, 604, 601, 12, 19, 19, 19, 605, 12, 12, 12, 606, + 607, 607, 607, 607, 607, 607, 607, 608, 607, 607, 607, 609, 607, 607, 609, 610, + 611, 611, 611, 611, 611, 611, 611, 612, 613, 613, 613, 613, 613, 613, 614, 615, + 616, 616, 616, 616, 616, 616, 617, 12, 618, 618, 618, 618, 618, 618, 619, 620, + 621, 621, 621, 621, 621, 621, 621, 622, 621, 623, 12, 624, 151, 154, 151, 625, + 151, 151, 151, 154, 626, 626, 626, 626, 626, 627, 626, 626, 626, 628, 12, 12, + 629, 629, 629, 629, 629, 629, 629, 12, 629, 629, 630, 631, 0, 317, 12, 12, + 29, 632, 29, 29, 633, 634, 632, 29, 412, 29, 635, 12, 636, 51, 635, 632, + 633, 634, 635, 635, 633, 634, 412, 29, 412, 29, 632, 637, 29, 29, 638, 29, + 29, 29, 29, 12, 632, 632, 638, 29, 50, 12, 12, 12, 12, 238, 0, 0, + 639, 12, 12, 12, 0, 0, 317, 0, 0, 0, 12, 12, 0, 0, 231, 238, + 0, 231, 317, 308, 0, 0, 0, 640, 0, 0, 231, 131, 641, 12, 12, 12, + 244, 244, 573, 12, 642, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0,1124,1125,1126,1127,1131,1133, 0,1147, @@ -5831,8 +4348,7 @@ _hb_ucd_u16[5080] = 96, 929, 802, 804, 806, 97, 98, 807, 930, 99, 931, 932, 933, 814, 100, 816, 817, 818, 819, 820, 821, 935, 0, 0, }; -static const int16_t -_hb_ucd_i16[92] = +static const int16_t _hb_ucd_i16[92]= { 0, 0, 1, -1, 2, 0, -2, 0, 0, 2, 0, -2, 0, 16, 0, -16, 0, 1, -1, 0, 3, 3, 3, -3, -3, -3, 0, 2016, 0, 2527, 1923, 1914, @@ -5842,40 +4358,1271 @@ _hb_ucd_i16[92] = 0,-2016,-2104, 0, 0,-2106,-2108,-2106,-2250, 0,-2527, 0, }; -static inline uint_fast8_t -_hb_ucd_gc (unsigned u) +static inline uint8_t _hb_ucd_gc (unsigned u) { - return u<1114112u?_hb_ucd_u8[5208+(((_hb_ucd_u8[1168+(((_hb_ucd_u16[((_hb_ucd_u8[544+(((_hb_ucd_u8[u>>1>>3>>3>>4])<<4)+((u>>1>>3>>3)&15u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; + return u<1114110 ? _hb_ucd_u8[6560u+((_hb_ucd_u8[816u+((_hb_ucd_u16[((_hb_ucd_u8[272u+((_hb_ucd_u8[((((((((u)>>1))>>3))>>4))>>4)])<<4)+((((((((u)>>1))>>3))>>4))&15)])<<4)+((((((u)>>1))>>3))&15)])<<3)+((((u)>>1))&7)])<<1)+((u)&1)] : 2; } -static inline uint_fast8_t -_hb_ucd_ccc (unsigned u) +static inline uint8_t _hb_ucd_ccc (unsigned u) { - return u<125259u?_hb_ucd_u8[7206+(((_hb_ucd_u8[6638+(((_hb_ucd_u8[6162+(((_hb_ucd_u8[5802+(((_hb_ucd_u8[5556+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; + return u<125259 ? _hb_ucd_u8[8620u+((_hb_ucd_u8[8036u+((_hb_ucd_u8[7556u+((_hb_ucd_u8[7188u+((_hb_ucd_u8[6942u+((((((((u)>>2))>>2))>>2))>>3)])<<3)+((((((((u)>>2))>>2))>>2))&7)])<<2)+((((((u)>>2))>>2))&3)])<<2)+((((u)>>2))&3)])<<2)+((u)&3)] : 0; } -static inline unsigned -_hb_ucd_b4 (const uint8_t* a, unsigned i) +static inline uint8_t _hb_ucd_b4 (const uint8_t* a, unsigned i) { - return (a[i>>1]>>((i&1u)<<2))&15u; + return (a[i>>1]>>((i&1)<<2))&15; } -static inline int_fast16_t -_hb_ucd_bmg (unsigned u) +static inline int16_t _hb_ucd_bmg (unsigned u) { - return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[8098+(((_hb_ucd_u8[7866+(((_hb_ucd_u8[7770+(((_hb_ucd_b4(7706+_hb_ucd_u8,u>>1>>2>>3>>3))<<3)+((u>>1>>2>>3)&7u))])<<3)+((u>>1>>2)&7u))])<<2)+((u>>1)&3u))])<<1)+((u)&1u)]:0; + return u<65380 ? _hb_ucd_i16[((_hb_ucd_u8[9516u+((_hb_ucd_u8[9284u+((_hb_ucd_u8[9188u+((_hb_ucd_b4(_hb_ucd_u8+9124u,((((((((u)>>1))>>2))>>3))>>3)))<<3)+((((((((u)>>1))>>2))>>3))&7)])<<3)+((((((u)>>1))>>2))&7)])<<2)+((((u)>>1))&3)])<<1)+((u)&1)] : 0; } -static inline uint_fast8_t -_hb_ucd_sc (unsigned u) +static inline uint8_t _hb_ucd_sc (unsigned u) { - return u<918016u?_hb_ucd_u8[11464+(((_hb_ucd_u8[10472+(((_hb_ucd_u8[9452+(((_hb_ucd_u8[8764+(((_hb_ucd_u8[8460+(((_hb_ucd_u8[8346+(u>>2>>2>>2>>3>>4)])<<4)+((u>>2>>2>>2>>3)&15u))])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:2; + return u<918000 ? _hb_ucd_u8[10950u+((_hb_ucd_u16[4648u+((_hb_ucd_u16[2608u+((_hb_ucd_u8[10214u+((_hb_ucd_u8[9764u+((((((((u)>>2))>>2))>>3))>>4)])<<4)+((((((((u)>>2))>>2))>>3))&15)])<<3)+((((((u)>>2))>>2))&7)])<<2)+((((u)>>2))&3)])<<2)+((u)&3)] : 2; } -static inline uint_fast16_t -_hb_ucd_dm (unsigned u) +static inline uint16_t _hb_ucd_dm (unsigned u) { - return u<195102u?_hb_ucd_u16[1656+(((_hb_ucd_u8[12834+(((_hb_ucd_u8[12452+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; + return u<195102 ? _hb_ucd_u16[7480u+((_hb_ucd_u8[13904u+((_hb_ucd_u8[13522u+((((u)>>4))>>5)])<<5)+((((u)>>4))&31)])<<4)+((u)&15)] : 0; } + +#else + +#include + +static const uint8_t _hb_ucd_u8[13937]= +{ + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 7, 11, 12, 12, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 7, 21, 22, 22, 22, 23, 24, 7, 7, + 7, 25, 22, 22, 22, 26, 27, 28, 22, 29, 30, 31, 32, 33, 34, 35, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 21, 22, 36, + 7, 7, 7, 7, 7, 7, 37, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 38, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 34, 34, 34, 35, 36, 37, 34, 34, 34, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 67, 68, 69, 67, 70, 71, + 67, 67, 62, 72, 62, 62, 73, 67, 74, 75, 76, 77, 78, 67, 67, 67, + 79, 80, 34, 81, 82, 83, 67, 67, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 84, 34, 34, 34, 34, + 85, 34, 34, 34, 34, 34, 34, 34, 34, 86, 34, 34, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 34, 34, 34, 34, 34, 34, 34, 34, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,100, 34, 34, 34, 34,101,102, 34, 34,103,104,105,106,107,108, + 34, 34,109,110,111,112,113,114,115,116,117,118, 34, 34, 34,119, + 120,121,122,123,124,125,126,127, 34,128,129,130,131,132,133,134, + 135,136,137,138,139,140,141,142,143,144,111,145,146,147,148,111, + 149,150,151,152,153,154,155,156,157,158,159,160,111,161,162,163, + 34, 34, 34, 34, 34, 34, 34, 34,164, 34, 34,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,165, + 34, 34, 34, 34, 34, 34, 34, 34,166, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 111,111,167,111,111,111,111,111,111,111,111,111,111,111,111,111, + 34, 34, 34, 34,168,169,170, 34,111,111,171,111,172,173,174,175, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,119, + 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111, 34,176,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111, 67,177, 67, 67, 67,178,179, 67, + 67, 67,180,181,182,131, 65,111,183,184,185,186,187,188,189,190, + 67, 67, 67, 67,191,192,111,111,111,111,111,111,111,111,193,111, + 194,195,196,111,111,197,111,111,111,198,111,199,111,200,111, 34, + 34,201,202,111,111,111,111,111,131,203,204,111, 34,205,111,111, + 67, 67,206, 67, 67,111, 67,207, 67, 67, 67, 67, 67, 67, 67, 67, + 67,208, 67, 67, 67, 67, 67,177,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111, + 209,111,195,195,111,111,111,111,111,111,111,111,111,111,111,111, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, + 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, + 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, + 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, + 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, + 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, + 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, + 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, + 16, 16, 36, 16, 16, 16, 16, 16, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 40, 40, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, + 39, 39, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, + 42, 42, 42, 42, 42, 42, 42, 42, 32, 32, 41, 32, 16, 43, 16, 10, + 40, 40, 40, 44, 11, 11, 11, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 45, 34, 32, 34, 11, + 32, 46, 42, 42, 47, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 11, 11, 11, 11, 48, 2, 2, 2, 16, 16, 16, 16, 49, 50, 51, 52, + 53, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 54, + 55, 56, 42, 55, 42, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 57, 2, 2, 2, 2, 2, 2, 58, 58, 58, 8, 9, 59, 2, 60, + 42, 42, 42, 42, 42, 56, 61, 2, 62, 36, 36, 36, 36, 63, 42, 42, + 7, 7, 7, 7, 7, 2, 2, 36, 64, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 65, 42, 42, 42, 66, 46, 42, 42, 67, 68, 69, 42, 42, 36, + 7, 7, 7, 7, 7, 36, 70, 71, 2, 2, 2, 2, 2, 2, 2, 72, + 63, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 64, 36, + 36, 36, 36, 42, 42, 42, 42, 42, 7, 7, 7, 7, 7, 36, 36, 36, + 36, 36, 36, 36, 36, 63, 42, 42, 42, 42, 39, 21, 2, 39, 68, 20, + 36, 36, 36, 42, 42, 68, 42, 42, 42, 42, 68, 42, 68, 42, 42, 42, + 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, 63, 42, 42, 2, + 36, 36, 36, 36, 73, 36, 36, 36, 58, 58, 58, 74, 42, 42, 42, 42, + 36, 36, 36, 36, 75, 42, 42, 42, 42, 74, 42, 42, 42, 42, 42, 42, + 42, 76, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 76, 64, 77, + 78, 42, 42, 42, 76, 77, 78, 77, 63, 42, 42, 42, 36, 36, 36, 36, + 36, 42, 2, 7, 7, 7, 7, 7, 79, 36, 36, 36, 36, 36, 36, 36, + 63, 77, 80, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 77, + 78, 42, 42, 76, 77, 77, 78, 36, 36, 36, 36, 81, 77, 77, 36, 36, + 36, 42, 42, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 52, 57, 42, + 42, 76, 80, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 77, + 78, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 64, 36, 36, 36, + 36, 36, 36, 7, 7, 7, 7, 7, 42, 36, 63, 2, 2, 2, 2, 2, + 78, 42, 42, 42, 76, 77, 78, 42, 59, 20, 20, 20, 82, 42, 42, 42, + 42, 77, 80, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 78, + 78, 42, 42, 76, 77, 77, 78, 42, 42, 42, 42, 76, 77, 77, 36, 36, + 71, 27, 27, 27, 27, 27, 27, 27, 42, 64, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 77, 76, 77, 77, 77, 77, 77, 78, 42, + 36, 36, 36, 81, 77, 77, 77, 77, 77, 77, 77, 7, 7, 7, 7, 7, + 27, 83, 60, 60, 52, 60, 60, 60, 76, 77, 64, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 64, 42, 76, 77, 77, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 36, 36, 36, 36, 7, 7, 7, 84, 27, 27, 27, 83, + 63, 77, 65, 36, 36, 36, 36, 36, 77, 77, 77, 76, 77, 77, 42, 42, + 42, 42, 76, 77, 77, 77, 36, 36, 85, 81, 77, 77, 77, 77, 77, 77, + 42, 77, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 63, 64, 77, + 78, 42, 42, 77, 77, 77, 78, 70, 60, 60, 36, 81, 27, 27, 27, 86, + 27, 27, 27, 27, 83, 36, 36, 36, 36, 36, 36, 36, 36, 42, 42, 76, + 77, 42, 42, 42, 77, 77, 77, 77, 7, 77, 2, 2, 2, 2, 2, 2, + 63, 36, 42, 42, 42, 42, 42, 87, 36, 36, 36, 68, 42, 42, 42, 56, + 7, 7, 7, 7, 7, 2, 2, 2, 63, 36, 42, 42, 42, 42, 64, 36, + 36, 36, 36, 39, 42, 42, 42, 42, 7, 7, 7, 7, 7, 7, 36, 36, + 70, 60, 2, 2, 2, 2, 2, 2, 2, 88, 88, 60, 42, 60, 60, 60, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 46, 46, 46, 4, 4, 77, + 63, 42, 42, 42, 42, 42, 42, 76, 42, 42, 56, 42, 36, 36, 63, 42, + 42, 42, 42, 42, 42, 42, 42, 60, 60, 60, 60, 69, 60, 60, 60, 60, + 2, 2, 88, 60, 21, 2, 2, 2, 36, 36, 36, 36, 36, 81, 78, 42, + 76, 42, 42, 42, 78, 76, 78, 64, 36, 36, 36, 77, 42, 36, 36, 42, + 64, 77, 80, 81, 77, 77, 77, 36, 63, 42, 64, 36, 36, 36, 36, 36, + 36, 76, 78, 76, 77, 77, 78, 81, 7, 7, 7, 7, 7, 77, 78, 60, + 16, 16, 16, 16, 16, 49, 43, 16, 36, 36, 36, 36, 36, 36, 63, 42, + 2, 2, 2, 2, 89, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 60, 60, 60, 60, 60, 60, 60, 60, 11, 11, 11, 11, 16, 16, 16, 16, + 90, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 70, 65, + 91, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 92, 93, 93, + 36, 36, 36, 36, 36, 57, 2, 94, 95, 36, 36, 36, 36, 36, 36, 36, + 36, 42, 76, 77, 77, 77, 77, 80, 36, 42, 96, 2, 2, 2, 2, 2, + 36, 42, 42, 42, 42, 42, 42, 42, 36, 36, 42, 78, 42, 42, 42, 77, + 77, 77, 77, 76, 78, 42, 42, 42, 42, 42, 2, 79, 2, 59, 63, 42, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 97, 2, 55, 42, 74, + 36, 75, 36, 36, 36, 36, 36, 36, 36, 36, 63, 64, 36, 36, 36, 36, + 36, 36, 36, 36, 63, 36, 36, 36, 42, 76, 77, 78, 76, 77, 77, 77, + 77, 76, 77, 77, 78, 42, 42, 42, 60, 60, 2, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 27, 27, 60, 36, 36, 36, 63, 76, 78, 42, 2, + 36, 36, 81, 76, 42, 42, 42, 42, 76, 76, 78, 42, 42, 42, 76, 77, + 77, 78, 42, 42, 42, 42, 42, 42, 2, 2, 2, 79, 2, 2, 2, 2, + 42, 42, 42, 42, 42, 42, 42, 98, 42, 42, 80, 36, 36, 36, 36, 36, + 36, 36, 76, 42, 42, 76, 76, 77, 77, 76, 80, 36, 36, 36, 36, 2, + 88, 60, 60, 60, 60, 46, 42, 42, 42, 42, 60, 60, 60, 60, 21, 2, + 42, 80, 36, 36, 36, 36, 36, 36, 81, 42, 42, 77, 42, 78, 42, 36, + 36, 36, 36, 76, 42, 77, 78, 78, 42, 77, 77, 77, 77, 77, 2, 2, + 36, 36, 77, 77, 77, 77, 42, 42, 42, 42, 77, 42, 42, 56, 2, 2, + 7, 7, 7, 7, 7, 7, 85, 36, 36, 36, 36, 36, 39, 39, 39, 2, + 16, 16, 16, 16, 34, 16, 16, 16, 42, 56, 42, 42, 42, 42, 42, 42, + 76, 42, 42, 42, 64, 36, 63, 36, 36, 36, 64, 81, 42, 36, 36, 36, + 16, 16, 16, 16, 16, 16, 39, 39, 39, 39, 39, 39, 39, 43, 16, 16, + 16, 16, 16, 16, 43, 16, 16, 16, 16, 16, 16, 16, 16, 99, 39, 39, + 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, + 16, 16, 16, 16, 34, 11, 11, 11, 16, 16, 16, 16,100,100,100,100, + 16, 16, 16, 16, 11, 11,101,102, 40, 16, 16, 16, 11, 11,101, 40, + 16, 16, 16, 16, 11, 11,103, 40,104,104,104,104,104,105, 58, 58, + 50, 50, 50, 2,106,107,106,107, 2, 2, 2, 2,108, 58, 58,109, + 2, 2, 2, 2,110,111, 2,112,113, 2,114,115, 2, 2, 2, 2, + 2, 9,113, 2, 2, 2, 2,116, 58, 58, 58, 58, 58, 58, 58, 58, + 117, 39, 27, 27, 27, 8,114,118, 27, 27, 27, 27, 27, 8,114, 93, + 20, 20, 20, 20, 20, 20, 20, 20, 42, 42, 42, 42, 42, 42,119, 47, + 98, 47, 98, 42, 42, 42, 42, 42, 60,120, 60,121, 60, 34, 11, 16, + 11, 32,121, 60, 45, 11, 11, 60, 60, 60,120,120,120, 11, 11,122, + 11, 11, 35, 36,123, 60, 16, 11, 8, 8, 45, 16, 16, 26, 60,124, + 94, 94, 94, 94, 94, 94, 94, 94, 94,125,126, 94,127, 60, 60, 60, + 8, 8,128, 60, 60, 8, 60, 60,128, 26, 60,128, 60, 60, 60,128, + 60, 60, 60, 60, 60, 60, 60, 8, 60,128,128, 60, 60, 60, 60, 60, + 60, 60, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 60, 60, 60, 60, 4, 4, 60, 60, 8, 60, 60, 60,129,130, 60, 60, + 60, 60, 60, 60, 60, 60,128, 60, 60, 60, 60, 60, 60, 26, 8, 8, + 8, 8, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 8, 8, + 8, 60, 60, 60, 60, 60, 60, 60, 27, 27, 27, 27, 27, 27, 60, 60, + 60, 60, 60, 60, 60, 27, 27, 27, 60, 60, 60, 26, 60, 60, 60, 60, + 26, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 8, 8, 8, 8, + 60, 60, 60, 60, 60, 60, 60, 26, 60, 60, 60, 60, 4, 4, 4, 4, + 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 60, 60, 60, 60, 60, 60, + 8, 8,114,131, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, + 8,114,132,132,132,132,132,132,132,132,132,132,131, 8, 8, 8, + 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, + 8, 8,128, 26, 8, 8,128, 60, 32, 11, 32, 34, 34, 34, 34, 11, + 32, 32, 34, 16, 16, 16, 39, 11, 32, 32,124, 60, 60,121, 34,133, + 42, 32, 16, 16, 49, 2, 89, 2, 36, 36, 36, 36, 36, 36, 36, 75, + 2, 2, 2, 2, 2, 2, 2, 55, 2,106,106, 2,110,111,106, 2, + 2, 2, 2, 6, 2, 97,106, 2,106, 4, 4, 4, 4, 2, 2, 79, + 2, 2, 2, 2, 2, 50, 2, 2, 97,134, 2, 2, 2, 2, 2, 2, + 60, 2,135,132,132,132,136, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 1, 2,137,138, 4, 4, 4, 4, 4, 60, 4, 4, 4, 4,139, 93, + 140, 94, 94, 94, 94, 42, 42, 77,141, 39, 39, 60, 94,142, 57, 60, + 71, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 63,143,144, 62, + 36, 36, 36, 36, 36, 57, 39, 62, 60, 27, 27, 60, 60, 60, 60, 60, + 27, 27, 27, 27, 27, 60, 60, 60, 60, 60, 60, 60, 27, 27, 27, 27, + 145, 27, 27, 27, 27, 27, 27, 27, 36, 36, 75, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36,146, 2, 32, 32, 32, 32, 32, 32, 32, 63, + 47,147, 42, 42, 42, 42, 42, 79, 32, 32, 32, 32, 32, 32, 39, 42, + 36, 36, 36, 94, 94, 94, 94, 94, 42, 2, 2, 2, 2, 2, 2, 2, + 40, 40, 40,144, 39, 39, 39, 39, 40, 32, 32, 32, 32, 32, 32, 32, + 16, 32, 32, 32, 32, 32, 32, 32, 43, 16, 16, 16, 34, 34, 34, 32, + 32, 32, 32, 32, 41,148, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, + 32, 32, 11, 11, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, + 48, 39,149, 35, 39, 35, 36, 36, 36, 64, 36, 64, 36, 63, 36, 36, + 36, 81, 78, 76, 60, 60, 42, 42, 27, 27, 27, 60,150, 60, 60, 60, + 36, 36, 2, 2, 2, 2, 2, 2, 77, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 77, 77, 77, 77, 77, 77, 77, 77, 42, 42, 42, 42, 42, 2, + 42, 36, 36, 36, 2, 65, 65, 63, 36, 36, 36, 42, 42, 42, 42, 2, + 36, 36, 36, 63, 42, 42, 42, 42, 42, 77, 77, 77, 77, 77, 77, 96, + 36, 63, 77, 42, 42, 77, 42, 77, 96, 2, 2, 2, 2, 2, 2, 79, + 7, 7, 7, 7, 7, 7, 7, 2, 36, 36, 63, 62, 36, 36, 36, 36, + 36, 36, 36, 36, 63, 42, 42, 76, 78, 76, 78, 42, 42, 42, 42, 42, + 36, 63, 36, 36, 36, 36, 76, 77, 7, 7, 7, 7, 7, 7, 2, 2, + 62, 36, 36, 70, 60, 81, 76, 36, 64, 42, 64, 63, 64, 36, 36, 42, + 36, 36, 36, 36, 36, 36, 75, 2, 36, 36, 36, 36, 36, 81, 42, 77, + 2, 75,151, 42, 42, 42, 42, 42, 16, 16, 16, 16, 16,102, 39, 39, + 16, 16, 16, 16, 99, 40, 40, 40, 36, 81, 78, 77, 76, 96, 78, 42, + 152,152,152,152,152,152,152,152,153,153,153,153,153,153,153,153, + 16, 16, 16, 16, 16, 16, 35, 64, 36, 36, 36, 36,154, 36, 36, 36, + 36, 40, 40, 40, 40, 40, 40, 40, 40, 22, 60, 60, 60, 60, 60, 60, + 60, 71, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,132, + 60, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 60, 60, 60, + 36, 36, 36, 36, 36, 36,150, 60, 2, 2, 2,135,115, 2, 2, 2, + 6,155,156,132,132,132,132,132,132,132,115,135,115, 2,112,157, + 2, 2, 2, 2,139,132,132,115, 2,158, 8, 8, 59, 2, 2, 2, + 36, 36, 36, 36, 36, 36, 36,159, 2, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18,114,115, 4, 2, 36, 36, 36, 36, 36, + 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 39, + 20,160, 52, 20, 26, 8,128, 60, 60, 60, 60, 60,161, 58, 60, 60, + 2, 2, 2, 89, 27, 27, 27, 27, 27, 27, 27, 83, 60, 60, 60, 60, + 94, 94,127, 27, 83, 60, 60, 60, 60, 60, 60, 60, 60, 27, 60, 60, + 60, 60, 60, 60, 60, 60, 46, 42,162,162,162,162,162,162,162,162, + 163, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 86, 36, + 138, 36, 36, 36, 36, 94, 94, 94, 36, 36, 36, 36, 36, 36, 36, 57, + 164, 94, 94, 94, 94, 94, 94, 94, 11, 11, 11, 32, 16, 16, 16, 16, + 36, 36, 36, 57, 27, 27, 27, 27, 36, 36, 36, 70,145, 27, 27, 27, + 36, 36, 36,165, 27, 27, 27, 27, 36, 36, 36, 36, 36,165, 27, 27, + 36, 36, 36, 27, 27, 27, 27, 30, 36, 36, 36, 36, 36, 36, 27, 36, + 63, 42, 42, 42, 42, 42, 42, 42, 36, 36, 36, 36, 42, 42, 42, 42, + 36, 36, 36, 36, 36, 36,165, 30, 36, 36, 36, 36, 36, 36,165, 27, + 36, 36, 36, 36, 71, 36, 36, 36, 36, 36, 63, 42, 42,163, 27, 27, + 36, 36, 36, 36, 57, 2, 2, 2, 36, 36, 36, 36, 27, 27, 27, 27, + 16, 16, 16, 16, 16, 27, 27, 27, 36, 36, 42, 42, 42, 42, 42, 42, + 7, 7, 7, 7, 7, 36, 36, 62, 11, 11, 11, 11,166, 42, 42,141, + 16, 16, 16, 16, 16, 16, 16, 8, 36, 36, 36, 36, 36, 63,167, 50, + 88, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 42, 42, 42, + 27, 27, 27, 86, 36, 36, 36, 36,163, 27, 30, 2, 2, 2, 2, 2, + 36, 42, 42, 2, 2, 2, 2, 2, 36, 36,165, 27, 27, 27, 27, 27, + 78, 80, 36, 36, 36, 36, 36, 36, 42, 42, 42, 56, 2, 2, 2, 2, + 2, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 7, 7, 7, + 64, 63, 64, 36, 36, 36, 36, 63, 77, 78, 42, 76, 78, 56, 72, 2, + 2, 42, 42, 42, 42, 42, 66, 58, 36, 36, 36, 63, 42, 42, 78, 42, + 42, 42, 42, 7, 7, 7, 7, 7, 2, 2, 81, 80, 36, 36, 36, 36, + 36, 63, 2, 36, 36, 36, 36, 36, 36, 81, 77, 42, 42, 42, 42, 76, + 80, 36, 57, 2, 55, 42, 56, 78, 7, 7, 7, 7, 7, 57, 57, 2, + 89, 27, 27, 27, 27, 27, 27, 27, 36, 36, 36, 36, 36, 36, 77, 78, + 42, 77, 76, 42, 2, 2, 2, 64, 36, 36, 36, 36, 36, 36, 36, 63, + 76, 77, 77, 77, 77, 77, 77, 77, 36, 36, 36, 81, 77, 77, 80, 36, + 36, 77, 77, 42, 42, 42, 42, 42, 36, 36, 36, 36, 77, 78, 42, 42, + 42, 77, 77, 77, 77, 77, 77, 76, 64, 64, 2, 2, 2, 2, 2, 2, + 55, 42, 42, 42, 42, 42, 42, 42, 36, 36, 81, 77, 42, 42, 42, 42, + 77, 42, 76, 64, 36, 57, 2, 2, 7, 7, 7, 7, 7, 2, 2, 64, + 77, 78, 42, 42, 76, 76, 77, 78, 76, 42, 36, 65, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 81, 77, 42, 42, 42, 77, 77, 42, 78, + 56, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 42, 42, + 77, 78, 42, 42, 42, 76, 78, 78, 56, 2, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 63, 78, 77, 42, 42, 42, 78, 57, 2, 2, 2, + 36, 36, 36, 36, 36, 36, 63, 78, 77, 42, 42, 78, 42, 42, 42, 42, + 7, 7, 7, 7, 7, 27, 2, 88, 42, 42, 42, 42, 78, 56, 2, 2, + 27, 27, 27, 27, 27, 27, 27, 86, 77, 77, 77, 77, 77, 78, 76, 64, + 80, 78, 2, 2, 2, 2, 2, 2, 81, 77, 42, 42, 42, 42, 77, 77, + 64, 65, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 63, 42, 42, 42, 42, 64, 36, 36, 36, 63, 42, 42, 76, 63, 42, 56, + 2, 2, 2, 55, 42, 42, 42, 42, 63, 42, 42, 76, 78, 42, 36, 36, + 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 76, 42, 2, 65, 2, + 76, 42, 76, 76, 77, 77, 77, 77, 57, 2, 2, 2, 2, 2, 2, 2, + 42, 42, 42, 42, 42, 42, 42, 78, 2, 36, 36, 36, 36, 36, 36, 36, + 42, 42, 42, 42, 76, 42, 42, 42, 76, 42, 78, 42, 42, 42, 42, 42, + 42, 42, 42, 63, 42, 42, 42, 42, 36, 36, 36, 36, 36, 77, 77, 77, + 42, 76, 78, 78, 36, 36, 36, 36, 36, 36, 36, 36, 75, 36, 36, 36, + 36, 63, 76, 96, 2, 2, 2, 2, 42, 81, 36, 36, 36, 36, 36, 36, + 36, 36, 77, 42, 42, 42, 42, 77, 76, 56, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 42, 42, 42, 27, 27, 83, 60, 60, 60, 52, 20, + 150, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 21, + 64, 36, 36, 63, 42, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 42, + 42, 42, 42, 42, 42, 77, 78, 42, 42, 42, 56, 2, 2, 2, 2, 2, + 42, 42, 42, 56, 2, 2, 60, 60, 39, 39, 88, 60, 60, 60, 60, 60, + 7, 7, 7, 7, 7,168, 27, 27, 27, 86, 36, 36, 36, 36, 36, 36, + 39, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 75,146, 2, + 27, 27, 27, 30, 2, 2, 2, 2, 11, 11, 11, 11, 11, 32, 16, 16, + 81, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 78, + 42, 67, 39, 39, 39, 39, 39, 39, 39, 79, 42, 42, 42, 42, 42, 42, + 77, 39, 94, 94, 94, 94, 94, 94, 36, 36, 36, 36, 36, 36, 46, 56, + 7, 7, 7, 7, 7, 60, 60, 60, 60, 60,169, 78, 42, 60,169, 77, + 77,170, 58, 58, 58, 74, 42, 42, 42, 69, 46, 42, 42, 42, 60, 60, + 60, 60, 60, 60, 60, 42, 42, 60, 60, 42, 69, 60, 60, 60, 60, 60, + 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 16, 11, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, + 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, + 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, + 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, + 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 16, 7, + 42, 42, 42, 69, 60, 46, 42, 42, 42, 42, 42, 42, 42, 42, 69, 60, + 60, 60, 46, 60, 60, 60, 60, 60, 60, 60, 69, 21, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 55, 42, 42, 16, 16, 16, 16, 16,123, 16, 16, + 42, 42, 42, 67, 39, 39, 39, 39, 7, 7, 7, 7, 7, 7, 7, 70, + 36, 36, 36, 36, 36, 36, 42, 42, 7, 7, 7, 7, 7, 7, 7,171, + 36, 36, 36, 36, 36, 75, 42, 42,172, 7, 7, 7, 7, 7, 7, 84, + 36, 63, 36, 64, 36, 36, 36, 42, 36, 36, 63, 42, 42, 42, 42, 75, + 16, 16, 42, 42, 42, 67, 39, 39, 27, 27, 27, 27, 27, 27,145, 27, + 173, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,145, + 27, 27, 27, 27, 27, 27, 83, 60, 60, 60, 60, 60, 60, 25, 40, 40, + 0, 0, 29, 21, 21, 21, 23, 21, 22, 18, 21, 25, 21, 17, 13, 13, + 25, 25, 25, 21, 21, 9, 9, 9, 9, 22, 21, 18, 24, 16, 24, 5, + 5, 5, 5, 22, 25, 18, 25, 0, 23, 23, 26, 21, 24, 26, 7, 20, + 25, 1, 26, 24, 26, 25, 15, 15, 24, 15, 7, 19, 15, 21, 9, 25, + 9, 5, 5, 25, 5, 9, 5, 7, 7, 7, 9, 8, 8, 5, 6, 6, + 24, 24, 6, 24, 12, 12, 6, 5, 9, 21, 25, 9, 26, 12, 11, 11, + 9, 6, 5, 21, 17, 17, 17, 26, 26, 23, 23, 12, 17, 12, 21, 12, + 12, 21, 7, 21, 1, 1, 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, + 12, 7, 21, 7, 12, 1, 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, + 21, 1, 24, 7, 1, 12, 7, 6, 12, 10, 10, 10, 10, 12, 21, 6, + 10, 7, 7, 10, 23, 7, 15, 26, 13, 21, 13, 7, 15, 7, 12, 23, + 21, 26, 21, 15, 17, 7, 29, 7, 7, 22, 18, 18, 14, 14, 14, 7, + 10, 21, 17, 21, 11, 12, 5, 6, 8, 8, 8, 24, 5, 24, 9, 24, + 29, 29, 29, 1, 20, 19, 22, 20, 27, 28, 1, 29, 21, 20, 19, 21, + 21, 16, 16, 21, 25, 22, 18, 21, 21, 29, 15, 6, 18, 6, 12, 11, + 9, 26, 26, 9, 26, 5, 7, 5, 5, 26, 14, 9, 5, 14, 14, 15, + 25, 26, 26, 22, 18, 26, 18, 25, 18, 22, 5, 12, 22, 21, 21, 22, + 18, 17, 26, 6, 7, 14, 17, 22, 26, 14, 17, 6, 14, 6, 12, 24, + 24, 6, 26, 15, 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, 6, 10, + 4, 4, 3, 3, 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, 7, 1, + 25, 24, 26, 1, 2, 2, 12, 15, 21, 14, 7, 15, 9, 12, 12, 17, + 13, 15, 26, 10, 10, 1, 13, 23, 7, 13, 23, 15, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, 14, 0, 0, 0, + 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, + 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, + 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, + 0, 36, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 43, 44, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, + 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, 6, 7, 0, 8, 9, 10, + 0, 11, 12, 13, 14, 15, 16, 17, 16, 18, 16, 19, 16, 19, 16, 19, + 0, 19, 16, 20, 16, 19, 21, 19, 0, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, + 0, 0, 0, 0, 0, 0, 34, 0, 0, 35, 0, 0, 36, 0, 37, 0, + 0, 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 0, 0, 47, 0, 0, + 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 50, 0, 51, + 0, 52, 53, 0, 54, 0, 0, 0, 0, 0, 0, 55, 56, 57, 0, 0, + 0, 0, 58, 0, 0, 59, 60, 61, 62, 63, 0, 0, 64, 65, 0, 0, + 0, 66, 0, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 70, 0, 71, + 0, 0, 72, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, 74, 75, + 0, 0, 0, 0, 76, 77, 0, 78, 79, 0, 0, 80, 81, 0, 82, 62, + 0, 83, 84, 0, 0, 85, 86, 87, 0, 88, 0, 89, 0, 90, 0, 0, + 51, 91, 51, 0, 92, 0, 93, 0, 0, 0, 81, 0, 0, 0, 94, 95, + 0, 96, 97, 98, 99, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0,100, + 101, 0, 0, 0, 0, 0, 0,102, 0, 0, 0, 0, 0, 0,103, 0, + 0, 0, 0, 0, 0,104,105, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,106, 0, 0,107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,108, + 109, 0, 0,110, 0, 0, 0, 0, 0, 0,111, 0,112, 0,105, 0, + 0, 0, 0, 0,113,114, 0, 0, 0, 0, 0, 0, 0,115, 0, 0, + 0,116, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0,118, 0,119, + 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 0, + 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, 14, 15, + 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, 0, 0, + 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 0, 0, 28, 29, + 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, 0, 35, 33, 0, + 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, 0, 0, + 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, 0, 43, + 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, + 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 52, 0, 53, + 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, 0, 0, + 57, 58, 0, 0, 0, 59, 60, 61, 62, 0, 0, 0, 0, 63, 52, 0, + 64, 65, 0, 0, 66, 0, 0, 0, 67, 68, 0, 0, 0, 69, 0, 70, + 71, 72, 73, 74, 1, 75, 0, 76, 77, 78, 0, 0, 79, 80, 0, 0, + 0, 81, 0, 0, 1, 1, 0, 0, 82, 0, 0, 83, 0, 0, 0, 0, + 79, 84, 0, 85, 0, 0, 0, 0, 0, 80, 86, 0, 87, 0, 52, 0, + 1, 80, 0, 0, 88, 0, 0, 89, 0, 0, 0, 0, 0, 90, 57, 0, + 0, 0, 0, 0, 0, 91, 92, 0, 0, 86, 0, 0, 33, 0, 0, 93, + 0, 0, 0, 0, 94, 0, 0, 0, 0, 49, 0, 0, 95, 0, 0, 0, + 0, 96, 97, 0, 0, 98, 0, 0, 99, 0, 0, 0,100, 0, 0, 0, + 101, 0, 0, 0,102, 0, 0, 0, 0,103,104, 95, 0, 0,105, 0, + 0, 0, 86, 0, 0,106, 0, 0, 0,107,108, 0, 0,109,110, 0, + 0, 0, 0, 0, 0,111, 0, 0,112, 0, 0, 0, 0,113, 33, 0, + 114,115,116, 57, 0, 0,117, 35, 0, 0,118, 0, 0, 0,119, 0, + 0, 0, 0, 0, 0,120, 0, 0,121, 0, 0, 0, 0,122, 90, 0, + 0, 0, 0, 0, 57, 0, 0, 0, 0, 52,123, 0, 0, 0, 0,124, + 0, 0,125, 0, 0, 0, 0,123, 0, 0,126, 0, 0, 0, 0, 0, + 81, 0, 0, 0, 0,127, 0, 0, 0,128, 0, 0, 0,129, 0,130, + 0, 0, 0, 0,131,132,133, 0,134, 0,135, 0, 0, 0,136,137, + 138, 0, 79, 0, 0, 0, 0, 0, 35, 0, 0, 0,139, 0, 0, 0, + 140, 0, 0, 0,141, 0, 0, 0,142,143, 0,144, 0, 0,145, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, + 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, + 1, 1, 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, + 27, 28, 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, + 1, 36, 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, + 0, 0, 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, + 0, 47, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, 0, 19, + 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, + 35, 1, 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, + 0, 59, 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, + 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, + 68, 0, 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, + 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, + 0, 0, 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, + 83, 0, 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, + 15, 86, 36, 10, 21, 1, 1, 1, 1, 41, 1, 21, 87, 0, 0, 55, + 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 88, 0, 0, 89, + 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, 90, 9, 12, 4, 91, 8, + 92, 47, 0, 58, 50, 0, 21, 1, 21, 93, 94, 1, 1, 1, 1, 95, + 96, 97, 98, 1, 99, 58, 81,100,101, 4, 58, 0, 0, 0, 0, 0, + 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, 0, 0,102,103, 0, 0, + 104, 0, 0, 1, 1, 50, 0, 0, 0, 38, 0, 63, 0, 0, 0, 0, + 0, 62, 0, 0,105, 68, 61, 0, 0, 0, 78, 0, 0, 0,106,107, + 58, 38, 81, 0, 0, 0, 0, 0, 0,108, 1, 14, 4, 12, 84, 0, + 0, 0, 0, 38, 90, 0, 0, 0, 0,109, 0, 0,110, 61, 0,111, + 0, 0, 0, 1, 0, 0, 0, 0, 49, 50, 0, 0, 19, 58, 0, 0, + 112, 51, 0,112, 14, 52,113, 41, 0, 0, 62, 0, 0, 61, 0, 0, + 114, 0, 90, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, 0,114, + 0, 0, 0, 0,115, 0, 0, 0, 78, 55, 0, 38, 1, 58, 1, 58, + 0, 0, 0, 0, 0, 88, 63, 89, 0, 0,116, 0, 0, 0, 55, 0, + 0, 0, 0,116, 0, 0, 0, 0, 61, 0, 0, 0, 0, 79, 0, 61, + 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, 0, 0, 8, 92, + 0, 0, 1, 90, 0, 0,117, 0, 0, 0, 0, 0, 0,118, 0,119, + 120,121,122, 0,105, 4,123, 49, 23, 0, 0, 0, 38, 50, 38, 58, + 0, 0, 1, 90, 1, 1, 1, 1, 39, 1, 48,106, 90, 0, 0, 0, + 0, 1, 0, 0, 0,124, 0, 0, 0,113, 19, 59, 0, 38, 0, 81, + 0, 0, 4,123, 0, 0, 0, 1,125, 0, 0, 0, 0, 0,230,230, + 230,230,230,232,220,220,220,220,232,216,220,220,220,220,220,202, + 202,220,220,220,220,202,202,220,220,220, 1, 1, 1, 1, 1,220, + 220,220,220,230,230,230,230,240,230,220,220,220,230,230,230,220, + 220, 0,230,230,230,220,220,220,220,230,232,220,220,230,233,234, + 234,233,234,234,233,230, 0, 0, 0,230, 0,220,230,230,230,230, + 220,230,230,230,222,220,230,230,220,220,230,222,228,230, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, + 25, 0,230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, + 30, 31, 32, 33, 34,230,230,220,220,230,220,230,230,220, 35, 0, + 0, 0, 0, 0,230,230,230, 0, 0,230,230, 0,220,230,230,220, + 0, 0, 0, 36, 0, 0,230,220,230,230,220,220,230,220,220,230, + 220,230,220,230,230, 0, 0,220, 0, 0,230,230, 0,230, 0,230, + 230,230,230,230, 0, 0, 0,220,220,220,230,220,220,220,230,230, + 0,220, 27, 28, 29,230, 7, 0, 0, 0, 0, 9, 0, 0, 0,230, + 220,230,230, 0, 0, 0, 0, 0,230, 0, 0, 84, 91, 0, 0, 0, + 0, 9, 9, 0, 0, 0, 0, 0, 9, 0,103,103, 9, 0,107,107, + 107,107,118,118, 9, 0,122,122,122,122,220,220, 0, 0, 0,220, + 0,220, 0,216, 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0, + 130,130,130,130, 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0, + 220, 0, 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, 0,230, + 0, 0, 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0,230, 0, + 0,220,230,220, 0,220,230,230,230,234, 0, 0, 9, 9, 0, 0, + 7, 0,230,230,230, 0,230, 0, 1, 1, 1, 0, 0, 0,230,234, + 214,220,202,230,230,230,230,230,232,228,228,220,218,230,233,220, + 230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230,220,230, + 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, 0, 0, + 0,220,230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, 0,220, + 0,230,230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, 0, 0, + 230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, 6, 6, + 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, 0,226, + 216,216,216,216,216, 0,220,220,220, 0,232,232,220,230,230,230, + 7, 0, 16, 17, 17, 33, 17, 49, 17, 17, 84, 97,135,145, 26, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17,177, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 5, 3, 3, 3, + 3, 3, 6, 7, 8, 3, 3, 3, 3, 3, 9, 10, 11, 12, 13, 3, + 3, 3, 3, 3, 3, 3, 3, 14, 3, 15, 3, 3, 3, 3, 3, 3, + 16, 17, 18, 19, 20, 21, 3, 3, 3, 22, 23, 24, 3, 3, 3, 3, + 3, 3, 25, 3, 3, 3, 3, 3, 3, 3, 3, 26, 3, 3, 27, 28, + 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, + 0, 3, 0, 0, 0, 0, 0, 4, 0, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, + 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 0, + 0, 14, 15, 16, 6, 0, 17, 18, 19, 19, 19, 20, 21, 22, 23, 24, + 19, 25, 0, 26, 27, 19, 19, 28, 29, 30, 0, 31, 0, 0, 0, 8, + 0, 0, 0, 0, 0, 0, 0, 19, 28, 0, 32, 33, 9, 34, 35, 19, + 0, 0, 36, 37, 38, 39, 40, 19, 0, 41, 42, 43, 44, 31, 0, 1, + 45, 42, 0, 0, 0, 0, 0, 32, 14, 14, 0, 0, 0, 0, 14, 0, + 0, 46, 47, 47, 47, 47, 48, 49, 47, 47, 47, 47, 50, 51, 52, 53, + 43, 21, 0, 0, 0, 0, 0, 0, 0, 54, 6, 55, 0, 14, 19, 1, + 0, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 19, 58, 31, 0, 0, + 0, 0, 0, 0, 0, 59, 14, 0, 0, 0, 0, 1, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 60, 61, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 2, 3, 0, 4, 5, 0, 0, 6, 0, 0, 0, 7, + 0, 0, 0, 1, 1, 0, 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, + 8, 9, 10, 11, 12, 0, 0, 0, 13, 0, 0, 0, 0, 14, 15, 16, + 17, 0, 0, 0, 1, 0, 0, 18, 19, 0, 0, 0, 20, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 8, 21, 9, + 0, 0, 22, 0, 0, 0, 0, 1, 0, 23, 24, 25, 0, 0, 26, 0, + 0, 0, 8, 21, 27, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 28, + 29, 30, 0, 31, 32, 20, 1, 1, 0, 0, 0, 8, 21, 9, 1, 4, + 5, 0, 0, 0, 33, 9, 0, 1, 1, 1, 0, 8, 21, 21, 21, 21, + 34, 1, 35, 21, 21, 21, 9, 36, 0, 0, 37, 38, 1, 0, 39, 0, + 0, 0, 1, 0, 1, 0, 0, 0, 0, 8, 21, 9, 1, 0, 0, 0, + 40, 0, 8, 21, 21, 21, 21, 21, 21, 21, 21, 9, 0, 1, 1, 1, + 1, 8, 21, 21, 21, 9, 0, 0, 0, 41, 0, 42, 43, 0, 0, 0, + 1, 44, 0, 0, 0, 45, 8, 9, 1, 0, 0, 0, 8, 21, 21, 21, + 9, 0, 1, 0, 1, 1, 8, 21, 21, 9, 0, 4, 5, 8, 9, 1, + 0, 0, 16, 50, 84,118,136,152,186,187,187,187,187,187,187,187, + 187,187,187,187,187,187,187,187,187,187,187,187,187,187, 12, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 13, 13, 13, + 13, 13, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 15, 16, 17, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 19, 32, 33, 33, 33, 33, 33, + 34, 19, 19, 19, 19, 19, 19, 35, 19, 36, 37, 38, 38, 38, 38, 38, + 38, 39, 40, 19, 19, 19, 19, 19, 19, 19, 41, 42, 19, 19, 43, 19, + 19, 19, 44, 45, 9, 46, 47, 48, 49, 50, 51, 52, 9, 9, 19, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 53, 19, 19, 53, 19, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 54, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 55, + 0, 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 0, + 1, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 32, 33, 33, 33, 34, 35, 35, 35, 35, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 2, 2, 51, 51, 52, + 53, 54, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, + 57, 56, 56, 56, 56, 56, 56, 58, 59, 60, 61, 56, 62, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 56, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 71, 62, 62, 62, 62, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 73, 74, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 32, + 32, 32, 32, 32, 32, 32, 32, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 62, 62, 62, 62, 88, 89, 89, 89, 90, 89, 91, 92, 93, 94, 95, + 95, 96, 97, 87, 98, 99,100,101,102,103,104,105,105,105, 2,106, + 107,108,109,110,111,112,113,114,115,116,117, 89,118,119,120,121, + 122,123,124,125,126,127,128,129,130, 87,131,132,133,134, 87,135, + 136,137,138,139,140,141,142,143,144,145,146, 87,147,148,149,150, + 150,150,150,150,150,150,150,150,150,150, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87,151,152,152,152,152,152,152,152,152,153, + 153,153,153,153, 87, 87, 87, 87, 87,154, 87, 87, 87, 87, 87,155, + 155,155,155,156,157,158,158, 87, 87,159, 87,160,161,162,163,164, + 164,164,164,164,164,164,164,164,164,164,164,164,164,165,165,165, + 165,164,164, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87,166,167, + 168,169,170,170,170, 87, 87,171,172, 87, 87, 87, 87, 87, 87, 56, + 56, 56, 56, 56, 56,173, 56, 56, 56,174,175, 51, 56, 56, 87,176, + 176,176,176,176,176, 87, 87, 87, 87, 87, 87, 87, 87, 2, 87,177, + 6,178, 87, 87,179, 87, 87, 87,180, 87,181, 87,182, 87, 33,183, + 183,184, 87, 87, 87, 87, 87, 56, 56, 56, 87, 89, 89, 87, 87, 56, + 56, 56, 56,185, 87, 56, 56, 62, 62, 62, 62, 62, 87, 87, 87, 62, + 87, 87, 87, 87, 87, 87, 87, 56, 87,186,186, 0, 1, 2, 2, 0, + 0, 0, 0, 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 0, 0, 7, 0, 8, + 8, 8, 8, 8, 8, 8, 9, 10, 11, 11, 11, 11, 11, 12, 11, 13, + 13, 13, 13, 13, 13, 13, 13, 14, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 15, 16, 16, 16, 16, 16, 17, 18, 18, 18, 18, 18, 18, 19, + 20, 21, 21, 22, 23, 21, 24, 21, 21, 21, 21, 21, 25, 21, 21, 26, + 26, 26, 26, 26, 21, 21, 21, 27, 27, 27, 27, 28, 28, 28, 28, 29, + 29, 29, 29, 30, 30, 26, 21, 21, 21, 21, 21, 21, 21, 31, 21, 32, + 32, 32, 32, 32, 33, 34, 32, 35, 35, 35, 35, 35, 35, 35, 35, 36, + 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 38, + 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 40, + 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 42, + 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 43, 43, 43, 44, + 44, 44, 45, 44, 44, 44, 44, 46, 46, 46, 46, 46, 46, 46, 46, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 47, 47, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 51, 52, + 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 56, + 56, 57, 57, 57, 57, 58, 57, 59, 59, 60, 61, 62, 62, 63, 63, 64, + 64, 64, 64, 64, 64, 64, 64, 65, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 55, 55, 55, 55, 55, 67, 67, 67, 67, 67, 68, 68, 68, 69, + 69, 69, 69, 69, 69, 64, 64, 70, 70, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 8, 8, 8, 8, 8, 72, 72, 72, 72, 72, 72, 72, 72, 73, + 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 75, 76, 76, 76, 13, + 50, 50, 50, 73, 77, 78, 79, 4, 4, 80, 4, 4, 81, 82, 83, 4, + 4, 4, 84, 8, 8, 8, 8, 11, 11, 11, 11, 11, 11, 11, 11, 85, + 0, 0, 0, 0, 0, 0, 86, 0, 4, 0, 0, 0, 8, 8, 8, 0, + 0, 87, 88, 89, 0, 4, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 90, 90, 90, 90, 90, 90, 90, 90, 91, + 91, 91, 91, 91, 91, 4, 4, 92, 92, 92, 92, 92, 92, 92, 92, 50, + 50, 50, 93, 93, 93, 93, 93, 53, 53, 53, 53, 53, 53, 13, 13, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 0, 95, + 0, 96, 97, 98, 99, 99, 99, 99,100,101,102,102,102,102,103,104, + 104,104,105, 52, 52, 52, 52, 52, 0,104,104, 0, 0, 0,102, 52, + 52, 0, 0, 0, 0, 52,106, 0, 0, 0, 0, 0,102,102,107,102, + 102,102,102,102,108, 0, 0, 94, 94, 94, 94, 0, 0, 0, 0,109, + 109,109,109,109,109,109,109,109,109,109,109,109,110,110,110,111, + 111,111,111,111,111,111,111,111,111,111,111, 13, 13, 13, 13, 13, + 13,112,112,112,112,112,112, 0, 0,113, 4, 4, 4, 4, 4,114, + 4, 4, 4, 4, 4, 4, 4,115,115,115, 0,116,116,116,116,117, + 117,117,117,117,117, 32, 32,118,118,119,120,120,120, 52, 52,121, + 121,121,121,122,121, 49, 49,123,123,123,123,123,123, 49, 49,124, + 124,124,124,124,124,125,125, 53, 53, 53, 4, 4,126,127, 54, 54, + 54, 54, 54,125,125,125,125,128,128,128,128,128,128,128,128, 4, + 129, 18, 18, 18, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21,130, 21, 21, 21, 21, 8, 0,131, 0, 0, 0, 0, 21, 21, + 21, 21, 21, 21, 21, 21,132, 0, 0, 1, 2, 1, 2,133,101,102, + 134, 52, 52, 52, 52, 0, 0,135,135,135,135,135,135,135,135, 0, + 0, 0, 0, 11, 11, 11, 11, 11, 0, 11, 11, 11, 0, 0,136,137, + 137,138,138,138,138,139, 0,140,140,140,141,141,142,142,142,143, + 143,144,144,144,144,144,144,145,145,145,145,145,146,146,146,147, + 147,147,148,148,148,148,148,149,149,149,150,150,150,150,151,151, + 151,151,151,152,152,152,152,153,153,153,153,153,153,153,153,154, + 154,154,154,155,155,156,156,157,157,157,157,157,157,158,158,159, + 159,160,160,161,161,161,161,162,162,163,163,163,163,163,163,164, + 164,164,164,164,164,165,165,166,166,166,166,167,167,167,167,168, + 168,168,168,169,169,170,170,171,171,171,171,171,171,171,171,172, + 172,172,172,172,172,172,172,173,173,173,173,173,173,173,173,174, + 174,174,174,175,175,175,175,175,175,175,175,175,175,175,175,176, + 176,176,176,177, 21, 21, 21,178,178,178,179,179,179,179,180,180, + 180,180,181,181,181,182,182,183,183,183,183,183,183,183,183,184, + 184,184,184,184,185,185,185,186,186,186,186,186,187,187,187,188, + 188,188,188,188,188,189, 43,190,190,190,190,190,190,190,190,191, + 191,191,192,192,192,192,192,193,193,193,194,193,193,193,193,195, + 195,195,195,195,195,195,195,196,196,196,196,196,196,196,196,197, + 197,197,197,197,197,197,197,198,198,198,198,198,198,198,198,199, + 199,199,199,199,199, 66, 66,200,200,200,200,200, 49, 49, 49,201, + 201,201,201,201,201,201,201,202,202,202,202,202,202,202,202,203, + 203,203,203,203,203,203,203,204,204,204,204,204,204,204,204,205, + 205,205,205,205,205,205,205,206,206,206,206,206,207,207,207,207, + 207,207, 55,208,208,208,208, 32, 32, 32, 32, 32, 32,188,188,209, + 209,209,209,209,209,209,209,210,210,210,210,210,210,210,211,211, + 211,211,211,211,211,211,211,212,212,212,212,212,212,213,213,213, + 213,213,214,214,214,214,214,215,215,215,215,215,215,215,215,216, + 216,216,216,216,216,216,216,110,110,110,110, 39, 39, 39, 39,217, + 217,217,217,217,217,217,217,218,218,218,218,218,218,218,218,219, + 219,219,219,219,219,219,219,220,220,220,220,220,220,220,220,221, + 221,221,221,221,221,221,221,112,112,112,112,112,112,112,112,112, + 112,112,112,222,222,222,223,223,223,223,223,223,224,224,224,225, + 225,225,225,225,225,225,225,226,226,226,226,226,226,226,226,227, + 227,227,227,227,227,227,227,227,227,228,228,228,228,228,228,229, + 229,229,229,229,229,229,229,229,229,229,229,229,229,230, 94,231, + 231,231,231,231,231,231,231,232,232,232,232,232,232,232,232,102, + 102,102,102,102,102,102,102,233, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99,102,234, 99,235,102,236,236, + 236,236,236,236,236,236,236,237,237,237,237,237,237,237,237,237, + 237, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 0, 0, 0, 0, + 0, 0, 0, 0, 0,238,239,240, 0,241, 0, 0, 0, 0, 0,242, + 242,242,242,242,242,242,242, 91, 91, 91, 13, 13, 13, 13, 13,243, + 243,243,243,243,243,243,243,244,244,244,244,245,245,245,245,246, + 246,246,246,246,246,246,246,247,247,247,247,247,247,247,247,248, + 248,248,248,248,248,248,248,249,249,249,249,249,249,249,249,250, + 250,250,250,250,250,250,250,251, 0, 0, 0, 0, 0, 0, 0, 8, + 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 1, 2, 2, 2, 2, + 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, 2, 2, 2, + 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, 8, 10, 8, + 11, 8, 8, 8, 8, 8, 8, 12, 13, 13, 13, 14, 14, 14, 14, 14, + 15, 14, 14, 16, 17, 17, 17, 17, 17, 17, 17, 18, 19, 19, 19, 19, + 19, 19, 19, 20, 21, 20, 22, 20, 20, 23, 23, 20, 20, 20, 20, 22, + 20, 24, 7, 7, 25, 20, 20, 26, 20, 20, 20, 20, 20, 20, 21, 27, + 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, + 31, 31, 31, 32, 20, 20, 20, 33, 33, 33, 33, 34, 35, 33, 33, 33, + 36, 33, 33, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, 40, + 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, 44, + 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 47, 48, + 48, 48, 48, 49, 49, 49, 49, 49, 50, 51, 49, 52, 52, 52, 52, 53, + 53, 53, 53, 53, 53, 54, 53, 55, 55, 55, 55, 56, 56, 56, 56, 57, + 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 60, 60, + 60, 61, 62, 63, 63, 63, 63, 64, 64, 64, 64, 64, 65, 0, 0, 66, + 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 70, 71, 71, 71, + 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 75, + 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 79, + 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 7, 7, 7, 83, + 7, 84, 85, 0, 84, 86, 0, 2, 87, 88, 2, 2, 2, 2, 89, 90, + 87, 91, 2, 2, 2, 92, 2, 2, 2, 2, 93, 0, 0, 0, 86, 1, + 0, 0, 94, 0, 95, 96, 0, 4, 0, 0, 0, 0, 0, 0, 4, 97, + 97, 97, 97, 98, 98, 98, 98, 13, 13, 13, 13, 99, 99, 99, 99,100, + 100,100,100, 0,101, 0, 0,102,100,103,104, 0, 0,100, 0,105, + 106,106,106,106,106,106,106,106,106,107,105,108,109,109,109,109, + 109,109,109,109,109,110,108,111,111,111,111,112, 55, 55, 55, 55, + 55, 55,113,109,109,109,110,109,109, 0, 0,114,114,114,114,115, + 115,115,115,116,116,116,116,117,117,117,117, 96, 2, 2, 2, 2, + 2, 94, 2,118,118,118,118,119,119,119,119,120,120,120,120,121, + 121,121,121,121,121,121,122,123,123,123,123,124,124,124,124,124, + 124,124,125,126,126,126,126,127,127,127,127,128,128,128,128, 2, + 2, 3, 2, 2,129,130, 0,131,131,131,131,132, 17, 17, 18, 20, + 20, 20,133, 7, 7, 7,134, 20, 20, 20, 23, 0,135,109,109,109, + 109,109,136,137,137,137,137, 0, 0, 0,138,139,139,139,139,140, + 140,140,140, 84, 0, 0, 0,141,141,141,141,142,142,142,142,143, + 143,143,143,144,144,144,144,145,145,145,145,146,146,146,146,147, + 147,147,147,148,148,148,148,149,149,149,149,150,150,150,150,151, + 151,151,151,152,152,152,152,153,153,153,153,154,154,154,154,155, + 155,155,155,156,156,156,156,157,157,157,157,158,158,158,158,159, + 159,159,159,160,160,160,160,161,161,161,161,162,162,162,162,163, + 163,163,163,164,164,164,164,165,165,165,165,166,166,166,166,167, + 167,167,167,168,168,168,168,169,169,169,169,170,170,170,170,171, + 171,171,171,172,172,172,172,173,173,173,173,174,174,174,174,175, + 175,175,175,176,176,176,176,177,177,177,177,178, 20, 20, 20,179, + 179,179,179,180,180,180,180,181,181,181,181,182,182,182,182,183, + 183,183,183,184,184,184,184,185,185,185,185,186,186,186,186,187, + 187,187,187,188,188,188,188,189,189,189,189,190, 45, 45, 45,191, + 191,191,191,192,192,192,192,193,193,193,193,194,194,194,194,194, + 194,195,194,196,196,196,196,197,197,197,197,198,198,198,198,199, + 199,199,199,200,200,200,200,201,201,201,201,202,202,202,202,203, + 203,203,203,204,204,204,204,205,205,205,205,206,206,206,206,207, + 207,207,207,208,208,208,208,209,209,209,209,210,210,210,210,211, + 211,211,211,212,212,212,212,213,213,213,213,214,214,214,214,215, + 215,215,215,216,216,216,216,217,217,217,217,218,218,218,218,219, + 219,219,219,220,220,220,220,221,221,221,221,222,222,222,222,223, + 223,223,223,224,224,224,224,225,225,225,225,226,226,226,226,227, + 227,227,227,228,228,228,228,229,229,229,229,230,230,230,230,231, + 232,232,232,233,233,233,233,232,232,232,232,234,106,106,106,235, + 106,106,106,106,236,109,109,237,237,237,237,238,238,238,238, 0, + 239, 86, 0, 0, 0,239, 7, 82,138, 7, 0, 0, 0,240, 86,241, + 241,241,241,242,242,242,242,243,243,243,243,244,244,244,244,245, + 245,245,245,246,246,246,246,247,247,247,247,248,248,248,248,249, + 249,249,249,250, 0, 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, 19, 0, 0, 0, 0, + 0, 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, 9, + 9, 0, 9, 9, 0, 9, 0, 9, 9, 55, 55, 55, 55, 55, 55, 6, + 6, 6, 6, 6, 1, 1, 6, 6, 4, 4, 4, 4, 4, 4, 4, 4, + 14, 14, 14, 14, 14, 14, 14, 3, 3, 3, 3, 3, 0, 3, 3, 0, + 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, 1, 1, 3, 3, 1, + 3, 3, 3, 37, 37, 37, 37, 38, 38, 38, 38, 64, 64, 64, 64, 90, + 90, 90, 90, 95, 95, 95, 95, 3, 3, 0, 3, 7, 7, 7, 7, 7, + 1, 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, 11, + 11, 11, 11, 10, 10, 10, 10, 21, 21, 21, 21, 22, 22, 22, 22, 23, + 23, 23, 23, 16, 16, 16, 16, 20, 20, 20, 20, 36, 36, 36, 36, 24, + 24, 24, 24, 24, 24, 24, 0, 18, 18, 18, 18, 25, 25, 25, 25, 25, + 0, 0, 0, 0, 25, 25, 25, 33, 33, 33, 33, 8, 8, 8, 8, 8, + 8, 8, 0, 12, 12, 12, 12, 30, 30, 30, 30, 29, 29, 29, 29, 28, + 28, 28, 28, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 0, 0, + 0, 35, 35, 45, 45, 45, 45, 44, 44, 44, 44, 44, 0, 0, 0, 43, + 43, 43, 43, 46, 46, 46, 46, 31, 31, 31, 31, 32, 32, 0, 0, 32, + 0, 32, 32, 32, 32, 32, 32, 48, 48, 48, 48, 52, 52, 52, 52, 58, + 58, 58, 58, 54, 54, 54, 54, 91, 91, 91, 91, 62, 62, 62, 62, 76, + 76, 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, 73, 73, 73, 73, 1, + 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, + 1, 0, 0, 19, 19, 9, 9, 9, 9, 9, 6, 19, 9, 9, 9, 9, + 9, 19, 19, 9, 9, 9, 19, 6, 19, 19, 19, 19, 19, 19, 9, 0, + 0, 0, 19, 0, 0, 9, 0, 0, 0, 19, 19, 27, 27, 27, 27, 56, + 56, 56, 56, 61, 61, 61, 61, 13, 13, 13, 13, 0, 13, 0, 13, 0, + 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 0, 15, 15, 15, 15, + 15, 15, 15, 15, 1, 1, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 0, 26, 26, 26, 26, 26, 12, 12, 12, 12, 12, 12, 0, 39, + 39, 39, 39, 86, 86, 86, 86, 77, 77, 77, 77, 79, 79, 79, 79, 60, + 60, 60, 60, 65, 65, 65, 65, 75, 75, 75, 75, 69, 69, 69, 69, 69, + 69, 0, 69, 74, 74, 74, 74, 84, 84, 84, 84, 84, 84, 84, 0, 68, + 68, 68, 68, 92, 92, 92, 92, 87, 87, 87, 87, 19, 9, 19, 19, 19, + 19, 0, 0, 2, 2, 2, 2, 19, 19, 19, 4, 3, 3, 0, 0, 1, + 1, 6, 6, 0, 0, 17, 17, 17, 17, 0, 0, 49, 49, 49, 49, 0, + 1, 1, 1, 71, 71, 71, 71, 67, 67, 67, 67, 42, 42, 42, 42, 41, + 41, 41, 41,118,118,118,118, 53, 53, 53, 53, 59, 59, 59, 59, 40, + 40, 40, 40, 51, 51, 51, 51, 50, 50, 50, 50,135,135,135,135,106, + 106,106,106,104,104,104,104,161,161,161,161,170,170,170,170,110, + 110,110,110, 47, 47, 47, 47, 81, 81, 81, 81,120,120,120,120,116, + 116,116,116,128,128,128,128, 66, 66, 66, 66, 72, 72, 72, 72,173, + 173,173,173, 98, 98, 98, 98, 97, 97, 97, 97, 57, 57, 57, 57, 88, + 88, 88, 88,117,117,117,117,112,112,112,112, 78, 78, 78, 78, 83, + 83, 83, 83, 82, 82, 82, 82,122,122,122,122, 89, 89, 89, 89,130, + 130,130,130,144,144,144,144,165,165,165,165,156,156,156,156,156, + 156, 3, 3,147,147,147,147,148,148,148,148,158,158,158,158,153, + 153,153,153,149,149,149,149, 94, 94, 94, 94, 85, 85, 85, 85,101, + 101,101,101, 96, 96, 96, 96,111,111,111,111,100,100,100,100,100, + 36, 36, 36,108,108,108,108,129,129,129,129,109,109,109,109,107, + 107,107,107,107,107,107, 1,171,171,171,171,137,137,137,137,124, + 124,124,124,123,123,123,123,114,114,114,114,102,102,102,102,126, + 126,126,126,142,142,142,142,125,125,125,125,154,154,154,154,150, + 150,150,150,141,141,141,141,140,140,140,140,121,121,121,121,169, + 169,169,169,133,133,133,133,134,134,134,134,138,138,138,138,143, + 143,143,143,175,175,175,175,145,145,145,145,163,163,163,163, 63, + 63, 63, 63,157,157,157,157, 80, 80, 80, 80,127,127,127,127,166, + 166,166,166,115,115,115,115,159,159,159,159,103,103,103,103,119, + 119,119,119,167,167,167,167,146,146,146,146,172,172,172,172, 99, + 99, 99, 99,136,139, 13, 13,155,155,155,155,136,136,136,136, 17, + 15, 15, 15, 17, 17, 15, 15, 15, 17, 17, 17,139,139,139,139,105, + 105,105,105, 0, 0, 0, 1, 0, 0, 1, 1,131,131,131,131,151, + 151,151,151,160,160,160,160,152,152,152,152,164,164,164,164,168, + 168,168,168,174,174,174,174,113,113,113,113,132,132,132,132, 15, + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, + 10, 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, + 17, 9, 9, 9, 9, 18, 9, 9, 9, 9, 9, 19, 20, 21, 9, 22, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 23, 9, 9, 9, 9, 9, 24, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 26, + 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, + 0, 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, 38, + 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, + 0, 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, + 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, + 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, 0, + 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101, + 102,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0,107, 0, 0, + 0,108, 0,109, 0,110, 0,111,112,113, 0,114, 0, 0, 0,115, + 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,118,119,120,121, 0,122,123,124,125,126, 0, + 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142, + 143,144,145,146,147,148,149,150,151,152,153,154,155,156,157, 0, + 0, 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,162, 0,163, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,164,165, 0, 0, 0, 0, + 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,167, 0, 0, 0,168,169, 0, 0,170, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,171, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,172, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,175, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,176,177, 0, 0, 0, 0,178,179, 0, 0, + 0,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194, + 195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210, + 211,212,213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 4, +}; +static const uint16_t _hb_ucd_u16[5104]= +{ + 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, + 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, + 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, + 13, 13, 13, 35, 9, 42, 11, 11, 43, 44, 32, 45, 46, 47, 47, 48, + 49, 50, 47, 47, 51, 32, 52, 53, 47, 47, 47, 47, 47, 54, 55, 56, + 57, 58, 47, 32, 59, 47, 47, 47, 47, 47, 60, 53, 61, 47, 62, 63, + 47, 64, 65, 66, 47, 67, 47, 47, 68, 69, 47, 47, 70, 32, 71, 32, + 72, 47, 47, 73, 74, 75, 76, 77, 78, 47, 47, 79, 80, 81, 82, 83, + 84, 47, 47, 85, 86, 87, 88, 89, 84, 47, 47, 79, 90, 47, 82, 91, + 92, 47, 47, 93, 94, 95, 82, 96, 97, 47, 47, 98, 99, 100, 101, 102, + 103, 47, 47, 104, 105, 106, 82, 107, 108, 47, 47, 93, 109, 110, 82, 111, + 112, 47, 47, 113, 114, 115, 82, 116, 92, 47, 47, 47, 117, 118, 101, 119, + 47, 47, 47, 120, 121, 122, 66, 66, 47, 47, 47, 123, 124, 125, 47, 47, + 126, 127, 128, 129, 47, 47, 47, 130, 131, 32, 32, 132, 133, 134, 66, 66, + 47, 47, 135, 136, 122, 137, 138, 139, 140, 141, 9, 9, 9, 11, 11, 142, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 143, 144, 145, + 47, 146, 9, 9, 9, 9, 9, 147, 148, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 149, 47, 150, 151, 47, 47, 47, 47, 152, 153, + 47, 154, 47, 155, 47, 156, 47, 156, 47, 47, 47, 157, 158, 159, 160, 145, + 161, 160, 47, 47, 162, 47, 47, 47, 163, 47, 164, 47, 47, 47, 47, 47, + 47, 47, 165, 166, 167, 47, 47, 47, 47, 47, 47, 47, 47, 168, 146, 146, + 47, 169, 47, 47, 47, 170, 171, 172, 160, 160, 173, 174, 32, 32, 32, 32, + 175, 47, 47, 176, 177, 122, 178, 179, 180, 47, 181, 61, 47, 47, 182, 183, + 47, 47, 184, 185, 186, 61, 47, 187, 188, 9, 9, 9, 66, 189, 190, 191, + 11, 11, 192, 27, 27, 27, 193, 194, 11, 195, 27, 27, 32, 32, 32, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 196, 13, 13, 13, 13, 13, 13, + 197, 197, 197, 197, 197, 198, 197, 11, 199, 199, 199, 200, 201, 202, 202, 201, + 203, 204, 205, 206, 207, 208, 209, 210, 211, 27, 212, 212, 212, 213, 214, 32, + 215, 216, 217, 218, 219, 145, 220, 220, 221, 222, 223, 146, 224, 225, 146, 226, + 227, 227, 227, 227, 227, 227, 227, 227, 228, 146, 229, 146, 146, 146, 146, 230, + 146, 231, 227, 232, 146, 233, 234, 146, 146, 146, 146, 146, 146, 146, 145, 145, + 145, 235, 146, 146, 146, 146, 236, 145, 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 237, 238, 146, 146, 239, 146, 146, 146, 146, 146, 146, 240, 146, + 146, 146, 146, 146, 146, 146, 241, 242, 145, 243, 146, 146, 244, 227, 245, 227, + 246, 247, 227, 227, 227, 248, 227, 249, 146, 146, 146, 227, 250, 146, 146, 146, + 9, 9, 9, 11, 11, 11, 251, 252, 13, 13, 13, 13, 13, 13, 253, 254, + 11, 11, 11, 47, 47, 47, 255, 256, 47, 47, 47, 47, 47, 47, 32, 32, + 257, 258, 259, 260, 261, 262, 263, 263, 264, 265, 266, 267, 268, 47, 47, 47, + 47, 269, 148, 47, 47, 47, 47, 270, 47, 271, 47, 47, 146, 146, 146, 47, + 146, 146, 272, 146, 273, 274, 146, 146, 272, 146, 146, 274, 146, 146, 146, 146, + 47, 47, 47, 47, 146, 146, 146, 146, 47, 275, 47, 47, 47, 47, 47, 47, + 47, 146, 146, 146, 146, 47, 47, 187, 276, 47, 61, 47, 13, 13, 277, 278, + 13, 279, 47, 47, 47, 47, 280, 281, 31, 282, 283, 284, 13, 13, 13, 285, + 286, 287, 288, 289, 290, 291, 9, 292, 293, 47, 294, 295, 47, 47, 47, 296, + 297, 47, 47, 298, 299, 160, 32, 300, 61, 47, 301, 47, 302, 303, 47, 47, + 72, 47, 47, 304, 305, 306, 307, 61, 47, 47, 308, 309, 310, 311, 47, 312, + 47, 47, 47, 313, 58, 314, 315, 316, 47, 47, 47, 11, 11, 317, 318, 11, + 11, 11, 11, 11, 47, 47, 319, 160, 320, 320, 320, 320, 320, 320, 320, 320, + 321, 321, 321, 321, 321, 321, 321, 321, 11, 322, 323, 47, 47, 47, 47, 47, + 47, 47, 47, 324, 325, 326, 47, 47, 47, 47, 47, 327, 146, 47, 47, 47, + 47, 328, 47, 47, 329, 146, 146, 330, 32, 331, 32, 332, 333, 334, 335, 47, + 47, 47, 47, 47, 47, 47, 47, 336, 337, 2, 3, 4, 5, 338, 339, 340, + 47, 341, 47, 47, 47, 47, 342, 343, 344, 145, 145, 345, 220, 220, 220, 346, + 347, 146, 146, 146, 146, 146, 146, 348, 349, 349, 349, 349, 349, 349, 349, 349, + 47, 47, 47, 47, 47, 47, 350, 145, 47, 47, 351, 47, 352, 47, 47, 60, + 47, 353, 47, 47, 47, 354, 220, 220, 9, 9, 147, 11, 11, 47, 47, 47, + 47, 47, 160, 9, 9, 147, 11, 11, 47, 47, 47, 47, 47, 47, 353, 9, + 9, 355, 11, 11, 47, 47, 47, 47, 27, 27, 27, 27, 27, 27, 27, 27, + 47, 47, 47, 47, 47, 356, 47, 357, 47, 47, 358, 145, 145, 145, 47, 359, + 47, 360, 47, 353, 47, 47, 47, 47, 47, 47, 47, 361, 145, 145, 145, 145, + 362, 47, 47, 363, 145, 66, 47, 364, 47, 365, 145, 145, 366, 47, 367, 66, + 47, 47, 47, 368, 47, 369, 47, 369, 47, 368, 144, 145, 145, 145, 145, 145, + 9, 9, 9, 9, 11, 11, 11, 370, 47, 47, 371, 160, 372, 9, 373, 11, + 374, 227, 227, 227, 227, 227, 227, 227, 145, 145, 145, 145, 145, 145, 145, 145, + 47, 47, 375, 47, 275, 376, 146, 377, 47, 365, 378, 47, 60, 379, 66, 47, + 380, 66, 66, 47, 381, 145, 47, 47, 382, 47, 47, 363, 383, 384, 385, 386, + 180, 47, 47, 387, 388, 47, 47, 160, 97, 47, 389, 390, 391, 47, 47, 392, + 180, 47, 47, 393, 394, 395, 396, 145, 47, 47, 397, 398, 362, 32, 32, 32, + 47, 47, 368, 47, 47, 399, 172, 160, 92, 47, 47, 113, 400, 401, 402, 32, + 47, 47, 47, 403, 404, 405, 406, 32, 47, 47, 47, 407, 408, 409, 47, 47, + 47, 47, 47, 410, 411, 160, 160, 160, 47, 47, 412, 413, 414, 415, 32, 32, + 47, 47, 47, 416, 417, 160, 66, 66, 47, 47, 418, 419, 160, 160, 160, 160, + 47, 420, 421, 422, 47, 47, 47, 47, 47, 47, 397, 423, 66, 66, 66, 66, + 9, 9, 9, 9, 11, 11, 128, 424, 47, 47, 47, 425, 426, 160, 160, 160, + 47, 47, 47, 47, 47, 427, 428, 429, 430, 47, 47, 431, 432, 433, 47, 47, + 434, 435, 66, 47, 47, 47, 47, 47, 66, 66, 66, 66, 66, 66, 436, 429, + 47, 47, 47, 47, 47, 47, 437, 160, 47, 47, 412, 438, 437, 128, 145, 439, + 47, 156, 440, 441, 32, 32, 32, 32, 47, 47, 47, 362, 442, 160, 47, 47, + 443, 444, 160, 47, 47, 445, 160, 160, 47, 47, 47, 47, 47, 47, 47, 446, + 447, 47, 47, 448, 449, 450, 32, 32, 47, 47, 47, 47, 145, 451, 452, 453, + 220, 220, 220, 220, 220, 220, 220, 66, 47, 47, 47, 47, 47, 47, 47, 437, + 47, 47, 47, 209, 454, 32, 47, 47, 47, 455, 456, 160, 160, 160, 160, 160, + 47, 47, 47, 47, 47, 47, 306, 47, 47, 47, 47, 47, 160, 47, 47, 457, + 47, 47, 47, 458, 459, 460, 461, 47, 27, 27, 27, 27, 462, 47, 463, 160, + 9, 9, 9, 9, 9, 9, 11, 11, 145, 464, 9, 465, 11, 11, 11, 11, + 47, 47, 47, 47, 399, 466, 429, 429, 467, 468, 27, 27, 27, 27, 469, 470, + 47, 471, 209, 209, 209, 209, 209, 209, 146, 146, 146, 146, 146, 146, 146, 472, + 146, 146, 146, 146, 146, 146, 146, 227, 32, 32, 32, 32, 32, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 473, 474, 475, 146, 476, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 477, 146, 146, 146, 9, 478, 11, 479, 480, 11, 197, 9, + 481, 482, 9, 483, 11, 9, 478, 11, 479, 480, 11, 197, 9, 481, 482, 9, + 483, 11, 9, 478, 11, 479, 480, 11, 197, 9, 481, 482, 9, 483, 11, 9, + 478, 11, 197, 9, 484, 485, 486, 487, 11, 488, 9, 489, 490, 491, 492, 11, + 493, 9, 494, 11, 495, 160, 160, 160, 32, 32, 32, 496, 32, 32, 497, 498, + 499, 500, 32, 32, 32, 32, 32, 32, 501, 11, 11, 11, 11, 11, 11, 11, + 32, 32, 32, 27, 27, 27, 27, 27, 32, 32, 32, 32, 32, 32, 32, 32, + 47, 47, 47, 502, 503, 146, 146, 146, 47, 47, 455, 32, 47, 47, 504, 505, + 47, 47, 47, 47, 47, 47, 506, 160, 47, 47, 47, 47, 47, 47, 455, 507, + 47, 47, 47, 47, 47, 47, 508, 509, 47, 47, 47, 47, 358, 32, 32, 32, + 9, 9, 481, 11, 510, 306, 66, 66, 145, 145, 511, 512, 145, 145, 145, 145, + 145, 145, 513, 145, 145, 145, 145, 145, 47, 47, 47, 47, 47, 47, 47, 227, + 514, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 515, + 146, 146, 146, 146, 146, 227, 227, 227, 209, 209, 209, 209, 209, 209, 209, 209, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, + 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0, + 1124,1125,1126,1127,1131,1133, 0,1147,1154,1155,1156,1161,1187,1188,1189,1193, + 0,1219,1226,1227,1228,1229,1233, 0, 0,1267,1268,1269,1273,1298, 0,1303, + 943,1128, 944,1129, 954,1139, 958,1143, 959,1144, 960,1145, 961,1146, 964,1149, + 0, 0, 973,1158, 974,1159, 975,1160, 983,1168, 978,1163, 988,1173, 990,1175, + 991,1176, 993,1178, 994,1179, 0, 0,1004,1190,1005,1191,1006,1192,1014,1199, + 1007, 0, 0, 0,1016,1201,1020,1206, 0,1022,1208,1025,1211,1023,1209, 0, + 0, 0, 0,1032,1218,1037,1223,1035,1221, 0, 0, 0,1044,1230,1045,1231, + 1049,1235, 0, 0,1058,1244,1064,1250,1060,1246,1066,1252,1067,1253,1072,1258, + 1069,1255,1077,1264,1074,1261, 0, 0,1083,1270,1084,1271,1085,1272,1088,1275, + 1089,1276,1096,1283,1103,1290,1111,1299,1115,1118,1307,1120,1309,1121,1310, 0, + 1053,1239, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1093, + 1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 949,1134,1010, + 1195,1050,1236,1090,1277,1341,1368,1340,1367,1342,1369,1339,1366, 0,1320,1347, + 1418,1419,1323,1350, 0, 0, 992,1177,1018,1204,1055,1241,1416,1417,1415,1424, + 1202, 0, 0, 0, 987,1172, 0, 0,1031,1217,1321,1348,1322,1349,1338,1365, + 950,1135, 951,1136, 979,1164, 980,1165,1011,1196,1012,1197,1051,1237,1052,1238, + 1061,1247,1062,1248,1091,1278,1092,1279,1071,1257,1076,1263, 0, 0, 997,1182, + 0, 0, 0, 0, 0, 0, 945,1130, 982,1167,1337,1364,1335,1362,1046,1232, + 1422,1423,1113,1301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 0, 10,1425, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0,1314,1427, 5,1434,1438,1443, 0,1450, 0,1455,1461, + 1514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1446,1458,1468,1476,1480,1486, + 1517, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1489,1503,1494,1500,1508, 0, + 0, 0, 0,1520,1521, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1526,1528, 0,1525, 0, 0, 0,1522, 0, 0, 0, 0,1536,1532,1539, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1534, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1556, 0, 0, 0, 0, 0, 0, + 1548,1550, 0,1547, 0, 0, 0,1567, 0, 0, 0, 0,1558,1554,1561, 0, + 0, 0, 0, 0, 0, 0,1568,1569, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1529,1551, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1523,1545,1524,1546, 0, 0,1527,1549, 0, 0,1570,1571,1530,1552,1531,1553, + 0, 0,1533,1555,1535,1557,1537,1559, 0, 0,1572,1573,1544,1566,1538,1560, + 1540,1562,1541,1563,1542,1564, 0, 0,1543,1565, 0, 0, 0, 0, 0, 0, + 0, 0,1606,1607,1609,1608,1610, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1613, 0,1611, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1620, 0, 0, 0, 0, 0, 0, + 0,1623, 0, 0,1624, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1614,1615,1616,1617,1618,1619,1621,1622, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1628,1629, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1625,1626, 0,1627, + 0, 0, 0,1634, 0, 0,1635, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1630,1631,1632, 0, 0,1633, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1639, 0, 0,1638,1640, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1636,1637, 0, 0, + 0, 0, 0, 0,1641, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1642,1644,1643, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1645, 0, 0, 0, 0, 0, 0, 0, + 1646, 0, 0, 0, 0, 0, 0,1648,1649, 0,1647,1650, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1651,1653,1652, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1654, 0,1655,1657,1656, 0, + 0, 0, 0,1659, 0, 0, 0, 0, 0, 0, 0, 0, 0,1660, 0, 0, + 0, 0,1661, 0, 0, 0, 0,1662, 0, 0, 0, 0,1663, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1658, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1664, 0,1665,1673, 0,1674, 0, 0, 0, 0, 0, 0, 0, + 0,1666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1668, 0, 0, 0, 0, 0, 0, 0, 0, 0,1669, 0, 0, + 0, 0,1670, 0, 0, 0, 0,1671, 0, 0, 0, 0,1672, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1667, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1675, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1676, 0,1677, 0,1678, 0,1679, 0,1680, 0, + 0, 0,1681, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1682, 0,1683, 0, 0, + 1684,1685, 0,1686, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 953,1138, 955,1140, 956,1141, 957,1142,1324,1351, 963,1148, 965,1150, 968,1153, + 966,1151, 967,1152,1378,1380,1379,1381, 984,1169, 985,1170,1420,1421, 986,1171, + 989,1174, 995,1180, 998,1183, 996,1181, 999,1184,1000,1185,1015,1200,1329,1356, + 1017,1203,1019,1205,1021,1207,1024,1210,1687,1688,1027,1213,1026,1212,1028,1214, + 1029,1215,1030,1216,1034,1220,1036,1222,1039,1225,1038,1224,1334,1361,1336,1363, + 1382,1384,1383,1385,1056,1242,1057,1243,1059,1245,1063,1249,1689,1690,1065,1251, + 1068,1254,1070,1256,1386,1387,1388,1389,1691,1692,1073,1259,1075,1262,1079,1266, + 1078,1265,1095,1282,1098,1285,1097,1284,1390,1391,1392,1393,1099,1286,1100,1287, + 1101,1288,1102,1289,1105,1292,1104,1291,1106,1294,1107,1295,1108,1296,1114,1302, + 1119,1308,1122,1311,1123,1312,1186,1260,1293,1305, 0,1394, 0, 0, 0, 0, + 952,1137, 947,1132,1317,1344,1316,1343,1319,1346,1318,1345,1693,1695,1371,1375, + 1370,1374,1373,1377,1372,1376,1694,1696, 981,1166, 977,1162, 972,1157,1326,1353, + 1325,1352,1328,1355,1327,1354,1697,1698,1009,1194,1013,1198,1054,1240,1048,1234, + 1331,1358,1330,1357,1333,1360,1332,1359,1699,1700,1396,1401,1395,1400,1398,1403, + 1397,1402,1399,1404,1094,1281,1087,1274,1406,1411,1405,1410,1408,1413,1407,1412, + 1409,1414,1109,1297,1117,1306,1116,1304,1112,1300, 0, 0, 0, 0, 0, 0, + 1471,1472,1701,1705,1702,1706,1703,1707,1430,1431,1715,1719,1716,1720,1717,1721, + 1477,1478,1729,1731,1730,1732, 0, 0,1435,1436,1733,1735,1734,1736, 0, 0, + 1481,1482,1737,1741,1738,1742,1739,1743,1439,1440,1751,1755,1752,1756,1753,1757, + 1490,1491,1765,1768,1766,1769,1767,1770,1447,1448,1771,1774,1772,1775,1773,1776, + 1495,1496,1777,1779,1778,1780, 0, 0,1451,1452,1781,1783,1782,1784, 0, 0, + 1504,1505,1785,1788,1786,1789,1787,1790, 0,1459, 0,1791, 0,1792, 0,1793, + 1509,1510,1794,1798,1795,1799,1796,1800,1462,1463,1808,1812,1809,1813,1810,1814, + 1467, 21,1475, 22,1479, 23,1485, 24,1493, 27,1499, 28,1507, 29, 0, 0, + 1704,1708,1709,1710,1711,1712,1713,1714,1718,1722,1723,1724,1725,1726,1727,1728, + 1740,1744,1745,1746,1747,1748,1749,1750,1754,1758,1759,1760,1761,1762,1763,1764, + 1797,1801,1802,1803,1804,1805,1806,1807,1811,1815,1816,1817,1818,1819,1820,1821, + 1470,1469,1822,1474,1465, 0,1473,1825,1429,1428,1426, 12,1432, 0, 26, 0, + 0,1315,1823,1484,1466, 0,1483,1829,1433, 13,1437, 14,1441,1826,1827,1828, + 1488,1487,1513, 19, 0, 0,1492,1515,1445,1444,1442, 15, 0,1831,1832,1833, + 1502,1501,1516, 25,1497,1498,1506,1518,1457,1456,1454, 17,1453,1313, 11, 3, + 0, 0,1824,1512,1519, 0,1511,1830,1449, 16,1460, 18,1464, 4, 0, 0, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1834,1835, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1836, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1837,1839,1838, + 0, 0, 0, 0,1840, 0, 0, 0, 0,1841, 0, 0,1842, 0, 0, 0, + 0, 0, 0, 0,1843, 0,1844, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1845, 0, 0,1846, 0, 0,1847, 0,1848, 0, 0, 0, 0, 0, 0, + 937, 0,1850, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1849, 936, 938, + 1851,1852, 0, 0,1853,1854, 0, 0,1855,1856, 0, 0, 0, 0, 0, 0, + 1857,1858, 0, 0,1861,1862, 0, 0,1863,1864, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1867,1868,1869,1870, + 1859,1860,1865,1866, 0, 0, 0, 0, 0, 0,1871,1872,1873,1874, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1875, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1877, 0,1878, 0, + 1879, 0,1880, 0,1881, 0,1882, 0,1883, 0,1884, 0,1885, 0,1886, 0, + 1887, 0,1888, 0, 0,1889, 0,1890, 0,1891, 0, 0, 0, 0, 0, 0, + 1892,1893, 0,1894,1895, 0,1896,1897, 0,1898,1899, 0,1900,1901, 0, 0, + 0, 0, 0, 0,1876, 0, 0, 0, 0, 0, 0, 0, 0, 0,1902, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1904, 0,1905, 0, + 1906, 0,1907, 0,1908, 0,1909, 0,1910, 0,1911, 0,1912, 0,1913, 0, + 1914, 0,1915, 0, 0,1916, 0,1917, 0,1918, 0, 0, 0, 0, 0, 0, + 1919,1920, 0,1921,1922, 0,1923,1924, 0,1925,1926, 0,1927,1928, 0, 0, + 0, 0, 0, 0,1903, 0, 0,1929,1930,1931,1932, 0, 0, 0,1933, 0, + 710, 385, 724, 715, 455, 103, 186, 825, 825, 242, 751, 205, 241, 336, 524, 601, + 663, 676, 688, 738, 411, 434, 474, 500, 649, 746, 799, 108, 180, 416, 482, 662, + 810, 275, 462, 658, 692, 344, 618, 679, 293, 388, 440, 492, 740, 116, 146, 168, + 368, 414, 481, 527, 606, 660, 665, 722, 781, 803, 809, 538, 553, 588, 642, 758, + 811, 701, 233, 299, 573, 612, 487, 540, 714, 779, 232, 267, 412, 445, 457, 585, + 594, 766, 167, 613, 149, 148, 560, 589, 648, 768, 708, 345, 411, 704, 105, 259, + 313, 496, 518, 174, 542, 120, 307, 101, 430, 372, 584, 183, 228, 529, 650, 697, + 424, 732, 428, 349, 632, 355, 517, 110, 135, 147, 403, 580, 624, 700, 750, 170, + 193, 245, 297, 374, 463, 543, 763, 801, 812, 815, 162, 384, 420, 730, 287, 330, + 337, 366, 459, 476, 509, 558, 591, 610, 726, 652, 734, 759, 154, 163, 198, 473, + 683, 697, 292, 311, 353, 423, 572, 494, 113, 217, 259, 280, 314, 499, 506, 603, + 608, 752, 778, 782, 788, 117, 557, 748, 774, 320, 109, 126, 260, 265, 373, 411, + 479, 523, 655, 737, 823, 380, 765, 161, 395, 398, 438, 451, 502, 516, 537, 583, + 791, 136, 340, 769, 122, 273, 446, 727, 305, 322, 400, 496, 771, 155, 190, 269, + 377, 391, 406, 432, 501, 519, 599, 684, 687, 749, 776, 175, 452, 191, 480, 510, + 659, 772, 805, 813, 397, 444, 619, 566, 568, 575, 491, 471, 707, 111, 636, 156, + 153, 288, 346, 578, 256, 435, 383, 729, 680, 767, 694, 295, 128, 210, 0, 0, + 227, 0, 379, 0, 0, 150, 493, 525, 544, 551, 552, 556, 783, 576, 604, 0, + 661, 0, 703, 0, 0, 735, 743, 0, 0, 0, 793, 794, 795, 808, 741, 773, + 118, 127, 130, 166, 169, 177, 207, 213, 215, 226, 229, 268, 270, 317, 327, 329, + 335, 369, 375, 381, 404, 441, 448, 458, 477, 484, 503, 539, 545, 547, 546, 548, + 549, 550, 554, 555, 561, 564, 569, 591, 593, 595, 598, 607, 620, 625, 625, 651, + 690, 695, 705, 706, 716, 717, 733, 735, 777, 786, 790, 315, 869, 623, 0, 0, + 102, 145, 134, 115, 129, 138, 165, 171, 207, 202, 206, 212, 227, 231, 240, 243, + 250, 254, 294, 296, 303, 308, 319, 325, 321, 329, 326, 335, 341, 357, 360, 362, + 370, 379, 388, 389, 393, 421, 424, 438, 456, 454, 458, 465, 477, 535, 485, 490, + 493, 507, 512, 514, 521, 522, 525, 526, 528, 533, 532, 541, 565, 569, 574, 586, + 591, 597, 607, 637, 647, 674, 691, 693, 695, 698, 703, 699, 705, 704, 702, 706, + 709, 717, 728, 736, 747, 754, 770, 777, 783, 784, 786, 787, 790, 802, 825, 848, + 847, 857, 55, 65, 66, 883, 892, 916, 822, 824, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1586, 0,1605, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1602,1603,1934,1935,1574,1575, + 1576,1577,1579,1580,1581,1583,1584, 0,1585,1587,1588,1589,1591, 0,1592, 0, + 1593,1594, 0,1595,1596, 0,1598,1599,1600,1601,1604,1582,1578,1590,1597, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1936, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1938, 0,1939, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1940, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1941,1942, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1943,1944, 0, 0, 0, + 0, 0, 0,1945, 0,1946, 0, 0, 0, 0, 0, 0, 0, 0,1947, 0, + 0,1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1950, 0,1949,1951, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1953,1952, 0,1954, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1955,1956, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1957, 0, 0, 0, 0, 0, 0, 0, + 0,1958,1961,1959,1965,1960,1962,1964,1963, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1967,1966,1968, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1969,1970, + 1971,1972,1973,1974,1975, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1976,1977,1978,1980,1979, + 1981, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 106, 104, 107, 826, 114, 118, 119, 121, 123, 124, 127, 125, 34, 830, 130, 131, + 132, 137, 827, 35, 133, 139, 829, 142, 143, 112, 144, 145, 924, 151, 152, 37, + 157, 158, 159, 160, 38, 165, 166, 169, 171, 172, 173, 174, 176, 177, 178, 179, + 181, 182, 182, 182, 833, 468, 184, 185, 834, 187, 188, 189, 196, 192, 194, 195, + 197, 199, 200, 201, 203, 204, 204, 206, 208, 209, 211, 218, 213, 219, 214, 216, + 153, 234, 221, 222, 223, 220, 225, 224, 230, 835, 235, 236, 237, 238, 239, 244, + 836, 837, 247, 248, 249, 246, 251, 39, 40, 253, 255, 255, 838, 257, 258, 259, + 261, 839, 262, 263, 301, 264, 41, 266, 270, 272, 271, 841, 274, 842, 277, 276, + 278, 281, 282, 42, 283, 284, 285, 286, 43, 843, 44, 289, 290, 291, 293, 934, + 298, 845, 845, 621, 300, 300, 45, 852, 894, 302, 304, 46, 306, 309, 310, 312, + 316, 48, 47, 317, 846, 318, 323, 324, 325, 324, 328, 329, 333, 331, 332, 334, + 335, 336, 338, 339, 342, 343, 347, 351, 849, 350, 348, 352, 354, 359, 850, 361, + 358, 356, 49, 363, 365, 367, 364, 50, 369, 371, 851, 376, 386, 378, 53, 381, + 52, 51, 140, 141, 387, 382, 614, 78, 388, 389, 390, 394, 392, 856, 54, 399, + 396, 402, 404, 858, 405, 401, 407, 55, 408, 409, 410, 413, 859, 415, 56, 417, + 860, 418, 57, 419, 422, 424, 425, 861, 840, 862, 426, 863, 429, 431, 427, 433, + 437, 441, 438, 439, 442, 443, 864, 436, 449, 450, 58, 454, 453, 865, 447, 460, + 866, 867, 461, 466, 465, 464, 59, 467, 470, 469, 472, 828, 475, 868, 478, 870, + 483, 485, 486, 871, 488, 489, 872, 873, 495, 497, 60, 498, 61, 61, 504, 505, + 507, 508, 511, 62, 513, 874, 515, 875, 518, 844, 520, 876, 877, 878, 63, 64, + 528, 880, 879, 881, 882, 530, 531, 531, 533, 66, 534, 67, 68, 884, 536, 538, + 541, 69, 885, 549, 886, 887, 556, 559, 70, 561, 562, 563, 888, 889, 889, 567, + 71, 890, 570, 571, 72, 891, 577, 73, 581, 579, 582, 893, 587, 74, 590, 592, + 596, 75, 895, 896, 76, 897, 600, 898, 602, 605, 607, 899, 900, 609, 901, 611, + 853, 77, 615, 616, 79, 617, 252, 902, 903, 854, 855, 621, 622, 731, 80, 627, + 626, 628, 164, 629, 630, 631, 633, 904, 632, 634, 639, 640, 635, 641, 646, 651, + 638, 643, 644, 645, 905, 907, 906, 81, 653, 654, 656, 911, 657, 908, 82, 83, + 909, 910, 84, 664, 665, 666, 667, 669, 668, 671, 670, 674, 672, 673, 675, 85, + 677, 678, 86, 681, 682, 912, 685, 686, 87, 689, 36, 913, 914, 88, 89, 696, + 702, 709, 711, 915, 712, 713, 718, 719, 917, 831, 721, 720, 723, 832, 725, 728, + 918, 919, 739, 742, 744, 920, 745, 753, 756, 757, 755, 760, 761, 921, 762, 90, + 764, 922, 91, 775, 279, 780, 923, 925, 92, 93, 785, 926, 94, 927, 787, 787, + 789, 928, 792, 95, 796, 797, 798, 800, 96, 929, 802, 804, 806, 97, 98, 807, + 930, 99, 931, 932, 933, 814, 100, 816, 817, 818, 819, 820, 821, 935, 0, 0, +}; +static const int16_t _hb_ucd_i16[92]= +{ + 0, 0, 1, -1, 2, 0, -2, 0, 0, 2, 0, -2, 0, 16, 0, -16, + 0, 1, -1, 0, 3, 3, 3, -3, -3, -3, 0, 2016, 0, 2527, 1923, 1914, + 1918, 0, 2250, 0, 0, 138, 0, 7, -7, 0, -1, 1, 1824, 0, 2104, 0, + 2108, 2106, 0, 2106, 1316, 0, -1, -138, 8, 8, 8, 0, 7, 7, -8, -8, + -8, -7,-1316, 1, -1, 3, -3, 1, 0,-1914,-1918, 0, 0,-1923,-1824, 0, + 0,-2016,-2104, 0, 0,-2106,-2108,-2106,-2250, 0,-2527, 0, +}; + +static inline uint8_t _hb_ucd_gc (unsigned u) +{ + return u<1114112 ? _hb_ucd_u8[5296u+((_hb_ucd_u8[1168u+((_hb_ucd_u16[((_hb_ucd_u8[544u+((_hb_ucd_u8[((((((((u)>>1))>>3))>>3))>>4)])<<4)+((((((((u)>>1))>>3))>>3))&15)])<<3)+((((((u)>>1))>>3))&7)])<<3)+((((u)>>1))&7)])<<1)+((u)&1)] : 2; +} +static inline uint8_t _hb_ucd_ccc (unsigned u) +{ + return u<125259 ? _hb_ucd_u8[7322u+((_hb_ucd_u8[6738u+((_hb_ucd_u8[6258u+((_hb_ucd_u8[5890u+((_hb_ucd_u8[5644u+((((((((u)>>2))>>2))>>2))>>3)])<<3)+((((((((u)>>2))>>2))>>2))&7)])<<2)+((((((u)>>2))>>2))&3)])<<2)+((((u)>>2))&3)])<<2)+((u)&3)] : 0; +} +static inline uint8_t _hb_ucd_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1)<<2))&15; +} +static inline int16_t _hb_ucd_bmg (unsigned u) +{ + return u<65380 ? _hb_ucd_i16[((_hb_ucd_u8[8218u+((_hb_ucd_u8[7986u+((_hb_ucd_u8[7890u+((_hb_ucd_b4(_hb_ucd_u8+7826u,((((((((u)>>1))>>2))>>3))>>3)))<<3)+((((((((u)>>1))>>2))>>3))&7)])<<3)+((((((u)>>1))>>2))&7)])<<2)+((((u)>>1))&3)])<<1)+((u)&1)] : 0; +} +static inline uint8_t _hb_ucd_sc (unsigned u) +{ + return u<918016 ? _hb_ucd_u8[11655u+((_hb_ucd_u8[10647u+((_hb_ucd_u8[9151u+((_hb_ucd_u8[8703u+((_hb_ucd_u8[8495u+((_hb_ucd_b4(_hb_ucd_u8+8466u,((((((((((u)>>2))>>2))>>3))>>3))>>4)))<<4)+((((((((((u)>>2))>>2))>>3))>>3))&15)])<<3)+((((((((u)>>2))>>2))>>3))&7)])<<3)+((((((u)>>2))>>2))&7)])<<2)+((((u)>>2))&3)])<<2)+((u)&3)] : 2; +} +static inline uint16_t _hb_ucd_dm (unsigned u) +{ + return u<195102 ? _hb_ucd_u16[1680u+((_hb_ucd_u8[13041u+((_hb_ucd_u8[12659u+((((u)>>4))>>5)])<<5)+((((u)>>4))&31)])<<4)+((u)&15)] : 0; +} + + #endif - #endif /* HB_UCD_TABLE_HH */ /* == End of generated table == */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh index 4bc8d64c28f..711dd9b6571 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh @@ -7,13 +7,13 @@ * on file with this header: * * # emoji-data.txt - * # Date: 2024-05-01, 21:25:24 GMT - * # © 2024 Unicode®, Inc. + * # Date: 2025-07-25, 17:54:31 GMT + * # © 2025 Unicode®, Inc. * # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. * # For terms of use and license, see https://www.unicode.org/terms_of_use.html * # * # Emoji Data for UTS #51 - * # Used with Emoji Version 16.0 and subsequent minor revisions (if any) + * # Version: 17.0 * # * # For documentation and usage, see https://www.unicode.org/reports/tr51 */ @@ -23,54 +23,62 @@ #include "hb-unicode.hh" -static const uint8_t -_hb_emoji_u8[464] = +#include + +static const uint8_t _hb_emoji_u8[624]= { 16, 17, 17, 17, 50, 20, 21, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,118,152, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2, 0, 3, 4, 0, 0, 5, 6, 0, 7, 0, 8, 9, 10, 11, 12, - 0, 0, 13, 0, 0, 0, 14, 0, 15, 0, 0, 0, 0, 16, 0, 0, - 17, 17, 18, 19, 20, 17, 17, 21, 17, 17, 22, 17, 23, 17, 24, 25, - 26, 27, 28, 17, 17, 17, 0, 0, 17, 17, 17, 17, 17, 17, 17, 29, - 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 4, 0, 0, - 5, 6, 0, 0, 7, 8, 0, 0, 8, 0, 9, 10, 0, 0, 11, 0, - 0, 12, 13, 14, 15, 16, 16, 16, 17, 16, 16, 16, 18, 19, 20, 21, - 22, 23, 0, 0, 0, 24, 0, 0, 25, 0, 26, 0, 0, 27, 0, 0, - 28, 0, 0, 0, 16, 16, 16, 16, 29, 9, 0, 30, 31, 32, 16, 33, - 34, 35, 36, 16, 16, 16, 16, 37, 16, 38, 39, 16, 16, 16, 40, 0, - 0, 0, 0, 41, 0, 0, 42, 16, 43, 0, 44, 0, 45, 46, 16, 16, - 47, 48, 49, 16, 16, 16, 16, 38, 0, 0, 0, 0, 0, 66, 0, 0, - 0, 0, 0, 16, 0, 2, 0, 0, 4, 0, 0, 2, 0, 0,240, 3, - 0, 6, 0, 0, 0, 0, 0, 12, 0, 1, 0, 0, 0,128, 0, 0, - 0,254, 15, 7, 4, 0, 0, 0, 0, 12, 64, 0, 1, 0, 0, 0, - 0, 0, 0,120,191,255,247,255,255,255,255,255, 63, 0,255,255, - 63,255, 87, 32, 2, 1, 24, 0,144, 80,184, 0,248, 0, 0, 0, - 0, 0,224, 0, 2, 0, 1,128, 0, 0, 48, 0,224, 0, 0, 24, - 0, 0, 33, 0, 0, 0, 1, 32, 0, 0,128, 2, 0,224, 0, 0, - 0,240, 3,192, 0, 64,254, 7, 0,224,255,255, 63, 0, 0, 0, - 254,255, 0, 4, 0,128,252,247, 0,254,255,255,255,255,255, 7, - 255,255,255, 63,192,255,255,255,255,255, 0, 0, 0, 0,240,255, - 0, 0,224,255, 0,240, 0, 0, 0,255, 0,252, 0,255, 0, 0, - 0,192,255,255, 0,240,255,255,255,255,255,247,191,255,255,255, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 3, 0, 0, 4, 0, 5, 0, 0, 0, 0, 0, 6, 0, 0, 7, + 0, 0, 0, 8, 0, 0, 9, 10, 11, 12, 13, 14, 15, 16, 17, 0, + 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 19, 20, 0, 0, + 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, + 23, 0, 24, 25, 0, 26, 27, 28, 29, 30, 31, 31, 32, 31, 33, 34, + 31, 31, 31, 35, 36, 37, 38, 39, 31, 40, 31, 41, 0, 0, 0, 42, + 43, 44, 45, 46, 47, 48, 31, 31, 0, 49, 31, 31, 0, 0, 0, 0, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 36, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 16, 0, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 0, 0, 2, 0, 0,240, 3, 0, 6, 0, 0, + 0, 0, 0, 12, 0, 1, 0, 0, 0,128, 0, 0, 0,254, 15, 7, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 64, 0, + 1, 0, 0, 0, 0, 0, 0,120, 31, 64, 50, 33, 77,196, 0, 7, + 5,255, 15,128,105, 1, 0,200, 0, 0,252, 26,131, 12, 3, 96, + 48,193, 26, 0, 0, 6,191, 39, 36,191, 84, 32, 2, 1, 24, 0, + 144, 80,184, 0, 24, 0, 0, 0, 0, 0,224, 0, 2, 0, 1,128, + 0, 0, 0, 0, 0, 0, 48, 0,224, 0, 0, 24, 0, 0, 0, 0, + 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, + 0, 0,128, 2, 0, 0, 0, 0, 16, 0, 0, 0, 0,240, 0, 0, + 0, 0,240,255, 0,128, 1, 0, 1,128, 1, 0, 0, 0,192,255, + 0, 0, 0, 0, 0, 0, 3,192, 0, 64,254, 7, 0,192,255,255, + 255,255,255,255, 63, 0, 0, 0,254,255, 0, 4, 0,128,252,247, + 0,254,255,255,192,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,243,255,255,255,255,255,207,206,255,255,255,255, + 255,255,255,255,255,255,185, 7,255,255,255,255,255,255,255,191, + 255,255,255,255,255,255,255, 63, 0,126,255,255,255,128,249, 7, + 128, 60, 97, 0, 48, 1, 6, 16, 28, 0, 14,112, 10,129, 8,252, + 255,255, 0, 0, 0, 0, 0, 0, 63,248,231,255, 63,250,249,255, + 0, 0, 0,252,255,255,255,255, 0,240, 0, 0, 0, 0, 0, 0, + 0,255, 0,252, 0, 0, 0, 0, 0,255, 0, 0, 0,192, 0,240, + 252,255, 0,254,255,255,255,255, 0,240,255,255,255,255,255,247, + 191,255,255,255,255,255,255,255, 0, 0, 0,255, 0,192,255,255, }; -static inline unsigned -_hb_emoji_b4 (const uint8_t* a, unsigned i) +static inline uint8_t _hb_emoji_b4 (const uint8_t* a, unsigned i) { - return (a[i>>1]>>((i&1u)<<2))&15u; + return (a[i>>1]>>((i&1)<<2))&15; } -static inline unsigned -_hb_emoji_b1 (const uint8_t* a, unsigned i) +static inline uint8_t _hb_emoji_b1 (const uint8_t* a, unsigned i) { - return (a[i>>3]>>((i&7u)<<0))&1u; + return (a[i>>3]>>((i&7)<<0))&1; } -static inline uint_fast8_t -_hb_emoji_is_Extended_Pictographic (unsigned u) +static inline uint8_t _hb_emoji_is_Extended_Pictographic (unsigned u) { - return u<131070u?_hb_emoji_b1(264+_hb_emoji_u8,((_hb_emoji_u8[144+(((_hb_emoji_u8[64+(((_hb_emoji_b4(_hb_emoji_u8,u>>5>>2>>3))<<3)+((u>>5>>2)&7u))])<<2)+((u>>5)&3u))])<<5)+((u)&31u)):0; + return u<131070 ? _hb_emoji_b1(_hb_emoji_u8+224u,((_hb_emoji_u8[64u+((_hb_emoji_b4(_hb_emoji_u8,((((u)>>6))>>4)))<<4)+((((u)>>6))&15)])<<6)+((u)&63)) : 0; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh b/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh index 924105001d7..9e6f89365fe 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh @@ -241,6 +241,57 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE } } + static hb_codepoint_t + vertical_char_for (hb_codepoint_t u) + { + switch (u >> 8) + { + case 0x20: switch (u) { + case 0x2013u: return 0xfe32u; // EN DASH + case 0x2014u: return 0xfe31u; // EM DASH + case 0x2025u: return 0xfe30u; // TWO DOT LEADER + case 0x2026u: return 0xfe19u; // HORIZONTAL ELLIPSIS + } break; + case 0x30: switch (u) { + case 0x3001u: return 0xfe11u; // IDEOGRAPHIC COMMA + case 0x3002u: return 0xfe12u; // IDEOGRAPHIC FULL STOP + case 0x3008u: return 0xfe3fu; // LEFT ANGLE BRACKET + case 0x3009u: return 0xfe40u; // RIGHT ANGLE BRACKET + case 0x300au: return 0xfe3du; // LEFT DOUBLE ANGLE BRACKET + case 0x300bu: return 0xfe3eu; // RIGHT DOUBLE ANGLE BRACKET + case 0x300cu: return 0xfe41u; // LEFT CORNER BRACKET + case 0x300du: return 0xfe42u; // RIGHT CORNER BRACKET + case 0x300eu: return 0xfe43u; // LEFT WHITE CORNER BRACKET + case 0x300fu: return 0xfe44u; // RIGHT WHITE CORNER BRACKET + case 0x3010u: return 0xfe3bu; // LEFT BLACK LENTICULAR BRACKET + case 0x3011u: return 0xfe3cu; // RIGHT BLACK LENTICULAR BRACKET + case 0x3014u: return 0xfe39u; // LEFT TORTOISE SHELL BRACKET + case 0x3015u: return 0xfe3au; // RIGHT TORTOISE SHELL BRACKET + case 0x3016u: return 0xfe17u; // LEFT WHITE LENTICULAR BRACKET + case 0x3017u: return 0xfe18u; // RIGHT WHITE LENTICULAR BRACKET + } break; + case 0xfe: switch (u) { + case 0xfe4fu: return 0xfe34u; // WAVY LOW LINE + } break; + case 0xff: switch (u) { + case 0xff01u: return 0xfe15u; // FULLWIDTH EXCLAMATION MARK + case 0xff08u: return 0xfe35u; // FULLWIDTH LEFT PARENTHESIS + case 0xff09u: return 0xfe36u; // FULLWIDTH RIGHT PARENTHESIS + case 0xff0cu: return 0xfe10u; // FULLWIDTH COMMA + case 0xff1au: return 0xfe13u; // FULLWIDTH COLON + case 0xff1bu: return 0xfe14u; // FULLWIDTH SEMICOLON + case 0xff1fu: return 0xfe16u; // FULLWIDTH QUESTION MARK + case 0xff3bu: return 0xfe47u; // FULLWIDTH LEFT SQUARE BRACKET + case 0xff3du: return 0xfe48u; // FULLWIDTH RIGHT SQUARE BRACKET + case 0xff3fu: return 0xfe33u; // FULLWIDTH LOW LINE + case 0xff5bu: return 0xfe37u; // FULLWIDTH LEFT CURLY BRACKET + case 0xff5du: return 0xfe38u; // FULLWIDTH RIGHT CURLY BRACKET + } break; + } + + return u; + } + struct { #define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_func_t name; HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS diff --git a/src/java.desktop/share/native/libharfbuzz/hb-utf.hh b/src/java.desktop/share/native/libharfbuzz/hb-utf.hh index 7fd3bf8828b..3891fb331af 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-utf.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-utf.hh @@ -37,7 +37,7 @@ struct hb_utf8_t typedef uint8_t codepoint_t; static constexpr unsigned max_len = 4; - static const codepoint_t * + static inline const codepoint_t * next (const codepoint_t *text, const codepoint_t *end, hb_codepoint_t *unicode, @@ -106,7 +106,7 @@ struct hb_utf8_t return text; } - static const codepoint_t * + static inline const codepoint_t * prev (const codepoint_t *text, const codepoint_t *start, hb_codepoint_t *unicode, @@ -185,7 +185,7 @@ struct hb_utf16_xe_t typedef TCodepoint codepoint_t; static constexpr unsigned max_len = 2; - static const codepoint_t * + static inline const codepoint_t * next (const codepoint_t *text, const codepoint_t *end, hb_codepoint_t *unicode, @@ -217,7 +217,7 @@ struct hb_utf16_xe_t return text; } - static const codepoint_t * + static inline const codepoint_t * prev (const codepoint_t *text, const codepoint_t *start, hb_codepoint_t *unicode, @@ -294,7 +294,7 @@ struct hb_utf32_xe_t typedef TCodepoint codepoint_t; static constexpr unsigned max_len = 1; - static const TCodepoint * + static inline const TCodepoint * next (const TCodepoint *text, const TCodepoint *end HB_UNUSED, hb_codepoint_t *unicode, @@ -306,7 +306,7 @@ struct hb_utf32_xe_t return text; } - static const TCodepoint * + static inline const TCodepoint * prev (const TCodepoint *text, const TCodepoint *start HB_UNUSED, hb_codepoint_t *unicode, @@ -353,7 +353,7 @@ struct hb_latin1_t typedef uint8_t codepoint_t; static constexpr unsigned max_len = 1; - static const codepoint_t * + static inline const codepoint_t * next (const codepoint_t *text, const codepoint_t *end HB_UNUSED, hb_codepoint_t *unicode, @@ -363,7 +363,7 @@ struct hb_latin1_t return text; } - static const codepoint_t * + static inline const codepoint_t * prev (const codepoint_t *text, const codepoint_t *start HB_UNUSED, hb_codepoint_t *unicode, @@ -405,7 +405,7 @@ struct hb_ascii_t typedef uint8_t codepoint_t; static constexpr unsigned max_len = 1; - static const codepoint_t * + static inline const codepoint_t * next (const codepoint_t *text, const codepoint_t *end HB_UNUSED, hb_codepoint_t *unicode, @@ -417,7 +417,7 @@ struct hb_ascii_t return text; } - static const codepoint_t * + static inline const codepoint_t * prev (const codepoint_t *text, const codepoint_t *start HB_UNUSED, hb_codepoint_t *unicode, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-vector.hh b/src/java.desktop/share/native/libharfbuzz/hb-vector.hh index cd27da96648..b650ca9f066 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-vector.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-vector.hh @@ -32,6 +32,12 @@ #include "hb-meta.hh" #include "hb-null.hh" +// Change to 1 to force inline vector allocs, to see callsite in malloc-stats tool. +#if 0 +#define HB_ALWAYS_INLINE_VECTOR_ALLOCS HB_ALWAYS_INLINE +#else +#define HB_ALWAYS_INLINE_VECTOR_ALLOCS +#endif template @@ -45,6 +51,7 @@ struct hb_vector_t using c_array_t = typename std::conditional, hb_array_t>::type; hb_vector_t () = default; + HB_ALWAYS_INLINE_VECTOR_ALLOCS hb_vector_t (std::initializer_list lst) : hb_vector_t () { alloc (lst.size (), true); @@ -57,18 +64,21 @@ struct hb_vector_t { extend (o); } + HB_ALWAYS_INLINE_VECTOR_ALLOCS hb_vector_t (const hb_vector_t &o) : hb_vector_t () { alloc_exact (o.length); if (unlikely (in_error ())) return; copy_array (o.as_array ()); } + HB_ALWAYS_INLINE_VECTOR_ALLOCS hb_vector_t (array_t o) : hb_vector_t () { alloc_exact (o.length); if (unlikely (in_error ())) return; copy_array (o); } + HB_ALWAYS_INLINE_VECTOR_ALLOCS hb_vector_t (c_array_t o) : hb_vector_t () { alloc_exact (o.length); @@ -84,8 +94,29 @@ struct hb_vector_t } ~hb_vector_t () { fini (); } + template + void + set_storage (Type (&array)[n]) + { set_storage (array, n); } + void + set_storage (hb_array_t array) + { set_storage (array.arrayZ, array.length); } + template + void + set_storage (Type *array, unsigned n) + { + assert (allocated == 0); + assert (length == 0); + + arrayZ = array; + length = n; + } + template + HB_ALWAYS_INLINE_VECTOR_ALLOCS void extend (const Iterable &o) { auto iter = hb_iter (o); @@ -106,12 +137,14 @@ struct hb_vector_t push_has_room (*iter++); } } + HB_ALWAYS_INLINE_VECTOR_ALLOCS void extend (array_t o) { alloc (length + o.length); if (unlikely (in_error ())) return; copy_array (o); } + HB_ALWAYS_INLINE_VECTOR_ALLOCS void extend (c_array_t o) { alloc (length + o.length); @@ -136,10 +169,7 @@ struct hb_vector_t void fini () { - /* We allow a hack to make the vector point to a foreign array - * by the user. In that case length/arrayZ are non-zero but - * allocated is zero. Don't free anything. */ - if (allocated) + if (is_owned ()) { shrink_vector (0); hb_free (arrayZ); @@ -147,11 +177,13 @@ struct hb_vector_t init (); } - void reset () + HB_ALWAYS_INLINE_VECTOR_ALLOCS + hb_vector_t &reset () { if (unlikely (in_error ())) reset_error (); resize (0); + return *this; } friend void swap (hb_vector_t& a, hb_vector_t& b) noexcept @@ -237,13 +269,16 @@ struct hb_vector_t Type * operator + (unsigned int i) { return arrayZ + i; } const Type * operator + (unsigned int i) const { return arrayZ + i; } + HB_ALWAYS_INLINE_VECTOR_ALLOCS Type *push () { if (unlikely (!resize (length + 1))) return std::addressof (Crap (Type)); return std::addressof (arrayZ[length - 1]); } - template Type *push (Args&&... args) + template + HB_ALWAYS_INLINE_VECTOR_ALLOCS + Type *push (Args&&... args) { if (unlikely ((int) length >= allocated && !alloc (length + 1))) // If push failed to allocate then don't copy v, since this may cause @@ -253,13 +288,20 @@ struct hb_vector_t return push_has_room (std::forward (args)...); } - template Type *push_has_room (Args&&... args) + template + HB_ALWAYS_INLINE_VECTOR_ALLOCS + Type *push_has_room (Args&&... args) { /* Emplace. */ Type *p = std::addressof (arrayZ[length++]); return new (p) Type (std::forward (args)...); } + bool is_owned () const + { + return allocated != 0 && allocated != -1; + } + bool in_error () const { return allocated < 0; } void set_error () { @@ -271,27 +313,40 @@ struct hb_vector_t assert (allocated < 0); allocated = -(allocated + 1); } + void ensure_error () + { + if (!in_error ()) + set_error (); + } - template Type * - realloc_vector (unsigned new_allocated, hb_priority<0>) + _realloc (unsigned new_allocated) { if (!new_allocated) { - hb_free (arrayZ); + if (is_owned ()) + hb_free (arrayZ); return nullptr; } + if (!allocated && arrayZ) + { + /* If we have a non-null arrayZ but allocated is 0, then we are + * reallocating from a foreign array. */ + Type *new_array = (Type *) hb_malloc (new_allocated * sizeof (Type)); + if (unlikely (!new_array)) + return nullptr; + hb_memcpy ((void *) new_array, (const void *) arrayZ, length * sizeof (Type)); + return new_array; + } return (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type)); } - template Type * - realloc_vector (unsigned new_allocated, hb_priority<0>) + _malloc_move (unsigned new_allocated) { if (!new_allocated) { - hb_free (arrayZ); + if (is_owned ()) + hb_free (arrayZ); return nullptr; } Type *new_array = (Type *) hb_malloc (new_allocated * sizeof (Type)); @@ -303,22 +358,33 @@ struct hb_vector_t new_array[i] = std::move (arrayZ[i]); arrayZ[i].~Type (); } - hb_free (arrayZ); + if (is_owned ()) + hb_free (arrayZ); } return new_array; } + + template + Type * + realloc_vector (unsigned new_allocated, hb_priority<0>) + { + return _realloc (new_allocated); + } + template + Type * + realloc_vector (unsigned new_allocated, hb_priority<0>) + { + return _malloc_move (new_allocated); + } /* Specialization for types that can be moved using realloc(). */ template Type * realloc_vector (unsigned new_allocated, hb_priority<1>) { - if (!new_allocated) - { - hb_free (arrayZ); - return nullptr; - } - return (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type)); + return _realloc (new_allocated); } template other) { - assert ((int) (length + other.length) <= allocated); hb_memcpy ((void *) (arrayZ + length), (const void *) other.arrayZ, other.length * item_size); length += other.length; } @@ -362,7 +427,6 @@ struct hb_vector_t void copy_array (hb_array_t other) { - assert ((int) (length + other.length) <= allocated); hb_memcpy ((void *) (arrayZ + length), (const void *) other.arrayZ, other.length * item_size); length += other.length; } @@ -372,7 +436,6 @@ struct hb_vector_t void copy_array (hb_array_t other) { - assert ((int) (length + other.length) <= allocated); for (unsigned i = 0; i < other.length; i++) new (std::addressof (arrayZ[length + i])) Type (other.arrayZ[i]); length += other.length; @@ -385,7 +448,6 @@ struct hb_vector_t void copy_array (hb_array_t other) { - assert ((int) (length + other.length) <= allocated); for (unsigned i = 0; i < other.length; i++) { new (std::addressof (arrayZ[length + i])) Type (); @@ -398,12 +460,12 @@ struct hb_vector_t shrink_vector (unsigned size) { assert (size <= length); - if (!std::is_trivially_destructible::value) + if (!hb_is_trivially_destructible(Type)) { unsigned count = length - size; - Type *p = arrayZ + length - 1; + Type *p = arrayZ + length; while (count--) - p--->~Type (); + (--p)->~Type (); } length = size; } @@ -416,6 +478,7 @@ struct hb_vector_t } /* Allocate for size but don't adjust length. */ + HB_ALWAYS_INLINE_VECTOR_ALLOCS bool alloc (unsigned int size, bool exact=false) { if (unlikely (in_error ())) @@ -471,17 +534,64 @@ struct hb_vector_t return true; } + HB_ALWAYS_INLINE_VECTOR_ALLOCS bool alloc_exact (unsigned int size) { return alloc (size, true); } + HB_ALWAYS_INLINE_VECTOR_ALLOCS void clear () { resize (0); } - bool resize (int size_, bool initialize = true, bool exact = false) + template + HB_ALWAYS_INLINE_VECTOR_ALLOCS + bool allocate_from_pool (allocator_t *allocator, unsigned size, unsigned int initialize = true) + { + if (allocator) + { + assert (!length && !allocated); + arrayZ = (Type *) allocator->alloc (size * sizeof (Type), alignof (Type)); + if (unlikely (!arrayZ)) + { + set_error (); + return false; + } + if (initialize) + grow_vector (size, hb_prioritize); + else + length = size; + return true; + } + return resize_full ((int) size, initialize, true); + } + + template + HB_ALWAYS_INLINE_VECTOR_ALLOCS + bool duplicate_vector_from_pool (allocator_t *allocator, const hb_vector_t &other) + { + if (unlikely (!allocate_from_pool (allocator, other.length, false))) + return false; + length = 0; + copy_array (other.as_array ()); + return true; + } + + template + void shrink_back_to_pool (allocator_t *allocator, int size) + { + unsigned orig_length = length; + + shrink (size, false); + + if (allocator && !is_owned ()) + allocator->discard (arrayZ + length, (orig_length - length) * sizeof (Type)); + } + + HB_ALWAYS_INLINE_VECTOR_ALLOCS + bool resize_full (int size_, bool initialize, bool exact) { unsigned int size = size_ < 0 ? 0u : (unsigned int) size_; if (!alloc (size, exact)) @@ -501,9 +611,20 @@ struct hb_vector_t length = size; return true; } - bool resize_exact (int size_, bool initialize = true) + HB_ALWAYS_INLINE_VECTOR_ALLOCS + bool resize (int size_) { - return resize (size_, initialize, true); + return resize_full (size_, true, false); + } + HB_ALWAYS_INLINE_VECTOR_ALLOCS + bool resize_dirty (int size_) + { + return resize_full (size_, false, false); + } + HB_ALWAYS_INLINE_VECTOR_ALLOCS + bool resize_exact (int size_) + { + return resize_full (size_, true, true); } Type pop () @@ -544,7 +665,7 @@ struct hb_vector_t shrink_vector (size); - if (shrink_memory) + if (is_owned () && shrink_memory) alloc_exact (size); /* To force shrinking memory if needed. */ } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-version.h b/src/java.desktop/share/native/libharfbuzz/hb-version.h index e41286d2d8c..c673d1e3612 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-version.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-version.h @@ -41,26 +41,26 @@ HB_BEGIN_DECLS * * The major component of the library version available at compile-time. */ -#define HB_VERSION_MAJOR 11 +#define HB_VERSION_MAJOR 12 /** * HB_VERSION_MINOR: * * The minor component of the library version available at compile-time. */ -#define HB_VERSION_MINOR 2 +#define HB_VERSION_MINOR 3 /** * HB_VERSION_MICRO: * * The micro component of the library version available at compile-time. */ -#define HB_VERSION_MICRO 0 +#define HB_VERSION_MICRO 2 /** * HB_VERSION_STRING: * * A string literal containing the library version available at compile-time. */ -#define HB_VERSION_STRING "11.2.0" +#define HB_VERSION_STRING "12.3.2" /** * HB_VERSION_ATLEAST: diff --git a/src/java.desktop/share/native/libharfbuzz/hb.hh b/src/java.desktop/share/native/libharfbuzz/hb.hh index 8c430e5770d..7582abaa933 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb.hh @@ -89,7 +89,6 @@ #pragma GCC diagnostic error "-Wstring-conversion" #pragma GCC diagnostic error "-Wswitch-enum" #pragma GCC diagnostic error "-Wtautological-overlap-compare" -#pragma GCC diagnostic error "-Wuninitialized" #pragma GCC diagnostic error "-Wunneeded-internal-declaration" #pragma GCC diagnostic error "-Wunused" #pragma GCC diagnostic error "-Wunused-local-typedefs" @@ -110,11 +109,21 @@ #pragma GCC diagnostic warning "-Wformat-signedness" #pragma GCC diagnostic warning "-Wignored-pragma-optimize" #pragma GCC diagnostic warning "-Wlogical-op" -#pragma GCC diagnostic warning "-Wmaybe-uninitialized" #pragma GCC diagnostic warning "-Wmissing-format-attribute" +#pragma GCC diagnostic warning "-Wpessimizing-move" #pragma GCC diagnostic warning "-Wundef" #pragma GCC diagnostic warning "-Wunsafe-loop-optimizations" #pragma GCC diagnostic warning "-Wunused-but-set-variable" +#ifdef __clang__ +// The following are too buggy on gcc +// https://github.com/harfbuzz/harfbuzz/issues/5589 +// https://github.com/harfbuzz/harfbuzz/pull/5367 +#pragma GCC diagnostic warning "-Wmaybe-uninitialized" +#pragma GCC diagnostic warning "-Wuninitialized" +#else +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#pragma GCC diagnostic ignored "-Wuninitialized" +#endif #endif /* Ignored currently, but should be fixed at some point. */ @@ -136,6 +145,7 @@ #pragma GCC diagnostic ignored "-Wformat-nonliteral" #pragma GCC diagnostic ignored "-Wformat-zero-length" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#pragma GCC diagnostic ignored "-Wold-style-cast" #pragma GCC diagnostic ignored "-Wpacked" // Erratic impl in clang #pragma GCC diagnostic ignored "-Wrange-loop-analysis" // https://github.com/harfbuzz/harfbuzz/issues/2834 #pragma GCC diagnostic ignored "-Wstrict-aliasing" @@ -239,6 +249,8 @@ // clang defines it so no need. #ifdef __has_builtin #define hb_has_builtin __has_builtin +#elif defined(_MSC_VER) +#define hb_has_builtin(x) 0 #else #define hb_has_builtin(x) ((defined(__GNUC__) && __GNUC__ >= 5)) #endif @@ -314,6 +326,10 @@ #endif #endif +#ifndef HB_HOT +#define HB_HOT __attribute__((hot)) +#endif + /* * Borrowed from https://bugzilla.mozilla.org/show_bug.cgi?id=1215411 * HB_FALLTHROUGH is an annotation to suppress compiler warnings about switch @@ -553,4 +569,13 @@ extern "C" void hb_free_impl(void *ptr); #include "hb-vector.hh" // Requires: hb-array hb-null #include "hb-object.hh" // Requires: hb-atomic hb-mutex hb-vector + +/* Our src/test-*.cc use hb_assert(), such that it's not compiled out under NDEBUG. + * https://github.com/harfbuzz/harfbuzz/issues/5418 */ +#define hb_always_assert(x) \ + HB_STMT_START { \ + if (!(x)) { fprintf(stderr, "Assertion failed: %s, at %s:%d\n", #x, __FILE__, __LINE__); abort(); } \ + } HB_STMT_END + + #endif /* HB_HH */ diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c index 03dba969e8d..6ed23ca7541 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c @@ -602,6 +602,9 @@ GtkApi* gtk3_load(JNIEnv *env, const char* lib_name) fp_g_uuid_string_is_valid = //since: 2.52 dl_symbol("g_uuid_string_is_valid"); fp_g_variant_print = dl_symbol("g_variant_print"); // since 2.24 + + fp_g_settings_new = dl_symbol("g_settings_new"); // since 2.26 + fp_g_settings_get_string = dl_symbol("g_settings_get_string"); // since 2.26 } fp_g_string_printf = dl_symbol("g_string_printf"); fp_g_strconcat = dl_symbol("g_strconcat"); @@ -2987,6 +2990,37 @@ static GdkWindow* gtk3_get_window(void *widget) { return fp_gtk_widget_get_window((GtkWidget*)widget); } +static gboolean apply_theme_if_needed() { + if (!glib_version_2_68) { + return FALSE; + } + + GSettings *settings = fp_g_settings_new("org.gnome.desktop.interface"); + if (!settings) { + return FALSE; + } + + static gboolean wasDark = FALSE; + + gchar *scheme = fp_g_settings_get_string(settings, "color-scheme"); + const gboolean isDark = strcmp(scheme, "prefer-dark") == 0; + + fp_g_free(scheme); + fp_g_object_unref(settings); + + if (wasDark ^ isDark) { + GtkSettings* gtkSettings = fp_gtk_settings_get_default(); + if (!gtkSettings) { + return FALSE; + } + fp_g_object_set(gtkSettings, "gtk-application-prefer-dark-theme", isDark, NULL); + wasDark = isDark; + return TRUE; + } + + return FALSE; +} + static void gtk3_init(GtkApi* gtk) { gtk->version = GTK_3; @@ -2996,6 +3030,8 @@ static void gtk3_init(GtkApi* gtk) { gtk->gtk_check_version = fp_gtk_check_version; gtk->get_setting = >k3_get_setting; + gtk->apply_theme_if_needed = &apply_theme_if_needed; + gtk->paint_arrow = >k3_paint_arrow; gtk->paint_box = >k3_paint_box; gtk->paint_box_gap = >k3_paint_box_gap; diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h index d3a83677dd6..637a3198e43 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h @@ -193,6 +193,7 @@ typedef void GtkMenuShell; typedef void GtkWidgetClass; typedef void PangoFontDescription; typedef void GtkSettings; +typedef void GSettings; typedef void GtkStyleProvider; typedef void cairo_pattern_t; typedef void cairo_t; @@ -633,6 +634,10 @@ static char* (*fp_pango_font_description_to_string)( const PangoFontDescription* fd); static GtkSettings* (*fp_gtk_settings_get_default)(); static GtkSettings* (*fp_gtk_widget_get_settings)(GtkWidget *widget); + +static GSettings *(*fp_g_settings_new)(const gchar *schema_id); +static gchar *(*fp_g_settings_get_string)(GSettings *settings, const gchar *key); + static GType (*fp_gtk_border_get_type)(); static void (*fp_gtk_arrow_set)(GtkWidget* arrow, GtkArrowType arrow_type, diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h index 39be6a735d7..37422094202 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h @@ -541,6 +541,8 @@ typedef struct GtkApi { guint required_micro); jobject (*get_setting)(JNIEnv *env, Setting property); + gboolean (*apply_theme_if_needed)(); + void (*paint_arrow)(WidgetType widget_type, GtkStateType state_type, GtkShadowType shadow_type, const gchar *detail, gint x, gint y, gint width, gint height, diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c b/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c index bb5c799f008..3b7b2880316 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c @@ -388,3 +388,17 @@ Java_com_sun_java_swing_plaf_gtk_GTKEngine_nativeSetRangeValue( gtk->set_range_value(widget_type, value, min, max, visible); gtk->gdk_threads_leave(); } + +/* + * Class: com_sun_java_swing_plaf_gtk_GTKLookAndFeel + * Method: applyThemeIfNeeded + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL +Java_com_sun_java_swing_plaf_gtk_GTKLookAndFeel_applyThemeIfNeeded(JNIEnv *env, jobject this) { + gtk->gdk_threads_enter(); + const gboolean result = gtk->apply_theme_if_needed(); + gtk->gdk_threads_leave(); + + return result; +} diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.java index 4fb843543e3..06574d17641 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2026, 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,8 +39,6 @@ import com.sun.java.swing.plaf.windows.TMSchema.Part; import com.sun.java.swing.plaf.windows.TMSchema.Prop; import com.sun.java.swing.plaf.windows.XPStyle.Skin; -import sun.awt.AppContext; - /** * A class to help mimic Vista theme animations. The only kind of * animation it handles for now is 'transition' animation (this seems @@ -68,8 +66,7 @@ final class AnimationController implements ActionListener, PropertyChangeListene Boolean.getBoolean("swing.disablevistaanimation"); - private static final Object ANIMATION_CONTROLLER_KEY = - new StringBuilder("ANIMATION_CONTROLLER_KEY"); + private static AnimationController animationController; private final Map> animationStateMap = new WeakHashMap>(); @@ -80,13 +77,10 @@ final class AnimationController implements ActionListener, PropertyChangeListene new javax.swing.Timer(1000/30, this); private static synchronized AnimationController getAnimationController() { - AppContext appContext = AppContext.getAppContext(); - Object obj = appContext.get(ANIMATION_CONTROLLER_KEY); - if (obj == null) { - obj = new AnimationController(); - appContext.put(ANIMATION_CONTROLLER_KEY, obj); + if (animationController == null) { + animationController = new AnimationController(); } - return (AnimationController) obj; + return animationController; } private AnimationController() { @@ -316,8 +310,7 @@ final class AnimationController implements ActionListener, PropertyChangeListene timer.stop(); UIManager.removePropertyChangeListener(this); synchronized (AnimationController.class) { - AppContext.getAppContext() - .put(ANIMATION_CONTROLLER_KEY, null); + animationController = null; } } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java index 0c86bd33e10..ee07276aa30 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -47,8 +47,6 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicButtonUI; import javax.swing.plaf.basic.BasicGraphicsUtils; -import sun.awt.AppContext; - import static com.sun.java.swing.plaf.windows.TMSchema.Part; import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; @@ -69,20 +67,13 @@ public final class WindowsButtonUI extends BasicButtonUI private boolean defaults_initialized = false; - private static final Object WINDOWS_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new WindowsButtonUI(); // ******************************** // Create PLAF // ******************************** public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - WindowsButtonUI windowsButtonUI = - (WindowsButtonUI) appContext.get(WINDOWS_BUTTON_UI_KEY); - if (windowsButtonUI == null) { - windowsButtonUI = new WindowsButtonUI(); - appContext.put(WINDOWS_BUTTON_UI_KEY, windowsButtonUI); - } - return windowsButtonUI; + return UI; } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java index 3c869efc97b..7cb2490fd76 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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,8 +30,6 @@ import javax.swing.JComponent; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; -import sun.awt.AppContext; - /** * Windows check box. * @@ -43,7 +41,7 @@ public final class WindowsCheckBoxUI extends WindowsRadioButtonUI // of BasicCheckBoxUI because we want to pick up all the // painting changes made in WindowsRadioButtonUI. - private static final Object WINDOWS_CHECK_BOX_UI_KEY = new Object(); + private static final ComponentUI UI = new WindowsCheckBoxUI(); private static final String propertyPrefix = "CheckBox" + "."; @@ -53,14 +51,7 @@ public final class WindowsCheckBoxUI extends WindowsRadioButtonUI // Create PLAF // ******************************** public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - WindowsCheckBoxUI windowsCheckBoxUI = - (WindowsCheckBoxUI) appContext.get(WINDOWS_CHECK_BOX_UI_KEY); - if (windowsCheckBoxUI == null) { - windowsCheckBoxUI = new WindowsCheckBoxUI(); - appContext.put(WINDOWS_CHECK_BOX_UI_KEY, windowsCheckBoxUI); - } - return windowsCheckBoxUI; + return UI; } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java index 403468af0af..4283f743b97 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -34,7 +34,6 @@ import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicLabelUI; -import sun.awt.AppContext; import sun.swing.MnemonicHandler; import sun.swing.SwingUtilities2; @@ -43,20 +42,13 @@ import sun.swing.SwingUtilities2; */ public final class WindowsLabelUI extends BasicLabelUI { - private static final Object WINDOWS_LABEL_UI_KEY = new Object(); + private static final ComponentUI UI = new WindowsLabelUI(); // ******************************** // Create PLAF // ******************************** public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - WindowsLabelUI windowsLabelUI = - (WindowsLabelUI) appContext.get(WINDOWS_LABEL_UI_KEY); - if (windowsLabelUI == null) { - windowsLabelUI = new WindowsLabelUI(); - appContext.put(WINDOWS_LABEL_UI_KEY, windowsLabelUI); - } - return windowsLabelUI; + return UI; } @Override diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java index 61f760d63c4..d15bc93a628 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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,15 @@ public final class WindowsMenuItemUI extends BasicMenuItemUI { SwingUtilities3.paintAccText(g, lh, lr, disabledForeground, acceleratorSelectionForeground, acceleratorForeground); + if (lh.getCheckIcon() != null && lh.useCheckAndArrow()) { + Rectangle rect = lr.getArrowRect(); + if (menuItem.getComponentOrientation().isLeftToRight()) { + rect.x += lh.getAfterCheckIconGap(); + } else { + rect.x -= lh.getAfterCheckIconGap(); + } + lr.setArrowRect(rect); + } SwingUtilities3.paintArrowIcon(g, lh, lr, foreground); // Restore original graphics font and color diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java index 2a784a470a4..c62a9ec8586 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -38,14 +38,12 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicGraphicsUtils; import javax.swing.plaf.basic.BasicRadioButtonUI; -import sun.awt.AppContext; - /** * Windows rendition of the component. */ public class WindowsRadioButtonUI extends BasicRadioButtonUI { - private static final Object WINDOWS_RADIO_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new WindowsRadioButtonUI(); protected int dashedRectGapX; protected int dashedRectGapY; @@ -60,14 +58,7 @@ public class WindowsRadioButtonUI extends BasicRadioButtonUI // Create PLAF // ******************************** public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - WindowsRadioButtonUI windowsRadioButtonUI = - (WindowsRadioButtonUI) appContext.get(WINDOWS_RADIO_BUTTON_UI_KEY); - if (windowsRadioButtonUI == null) { - windowsRadioButtonUI = new WindowsRadioButtonUI(); - appContext.put(WINDOWS_RADIO_BUTTON_UI_KEY, windowsRadioButtonUI); - } - return windowsRadioButtonUI; + return UI; } // ******************************** diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java index 5e94ebdb2cc..67eb5c1d6a0 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -38,8 +38,6 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicGraphicsUtils; import javax.swing.plaf.basic.BasicToggleButtonUI; -import sun.awt.AppContext; - /** * A Windows toggle button. * @@ -54,19 +52,12 @@ public final class WindowsToggleButtonUI extends BasicToggleButtonUI protected Color focusColor; - private static final Object WINDOWS_TOGGLE_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new WindowsToggleButtonUI(); private boolean defaults_initialized = false; public static ComponentUI createUI(JComponent b) { - AppContext appContext = AppContext.getAppContext(); - WindowsToggleButtonUI windowsToggleButtonUI = - (WindowsToggleButtonUI) appContext.get(WINDOWS_TOGGLE_BUTTON_UI_KEY); - if (windowsToggleButtonUI == null) { - windowsToggleButtonUI = new WindowsToggleButtonUI(); - appContext.put(WINDOWS_TOGGLE_BUTTON_UI_KEY, windowsToggleButtonUI); - } - return windowsToggleButtonUI; + return UI; } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java b/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java index 01be8587685..1da9db3a35b 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -1733,6 +1733,10 @@ public final class WPrinterJob extends RasterPrinterJob if (isRangeSet) { attributes.add(new PageRanges(from, to)); setPageRange(from, to); + } else { + attributes.remove(PageRanges.class); + setPageRange(Pageable.UNKNOWN_NUMBER_OF_PAGES, + Pageable.UNKNOWN_NUMBER_OF_PAGES); } defaultCopies = false; attributes.add(new Copies(copies)); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java index 065d045b57c..2bc759a920a 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -28,6 +28,7 @@ import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.Collections; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.NavigableMap; import java.util.NavigableSet; @@ -276,6 +277,28 @@ final class PeerConnIdManager { } } + /** + * {@return the list of stateless reset tokens associated with active peer connection IDs} + */ + public List activeResetTokens() { + lock.lock(); + try { + // we only support one active connection ID at the time + PeerConnectionId cid = peerConnectionIds.get(activeConnIdSeq); + byte[] statelessResetToken = null; + if (cid != null) { + statelessResetToken = cid.getStatelessResetToken(); + } + if (statelessResetToken != null) { + return List.of(statelessResetToken); + } else { + return List.of(); + } + } finally { + lock.unlock(); + } + } + /** * {@return the active peer connection ID} */ diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java index 580bbe23d31..edb94d5929a 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -1246,7 +1246,7 @@ public class QuicConnectionImpl extends QuicConnection implements QuicPacketRece final PacketSpace space = packetSpace(PacketNumberSpace.APPLICATION); final int maxDatagramSize = getMaxDatagramSize(); final QuicConnectionId peerConnectionId = peerConnectionId(); - final int dstIdLength = peerConnectionId().length(); + final int dstIdLength = peerConnectionId.length(); if (!canSend()) { return false; } @@ -1747,6 +1747,11 @@ public class QuicConnectionImpl extends QuicConnection implements QuicPacketRece return localConnIdManager.connectionIds(); } + @Override + public List activeResetTokens() { + return peerConnIdManager.activeResetTokens(); + } + LocalConnIdManager localConnectionIdManager() { return localConnIdManager; } @@ -2453,7 +2458,7 @@ public class QuicConnectionImpl extends QuicConnection implements QuicPacketRece } return; } - final QuicConnectionId currentPeerConnId = this.peerConnIdManager.getPeerConnId(); + final QuicConnectionId currentPeerConnId = peerConnectionId(); if (rt.sourceId().equals(currentPeerConnId)) { if (debug.on()) { debug.log("Invalid retry, same connection ID"); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java index 91b4a678a23..ef342d4cb56 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -1572,14 +1572,14 @@ public abstract sealed class QuicEndpoint implements AutoCloseable private void dropPeerIssuedResetTokensFor(QuicPacketReceiver connection) { // remove references to this connection from the map which holds the peer issued // reset tokens - peerIssuedResetTokens.values().removeIf(conn -> connection == conn); + connection.activeResetTokens().forEach(this::forgetStatelessResetToken); } - // remap peer issued stateless token from connection `from` to connection `to` - private void remapPeerIssuedResetToken(QuicPacketReceiver from, QuicPacketReceiver to) { - assert from != null; - assert to != null; - peerIssuedResetTokens.replaceAll((tok, c) -> c == from ? to : c); + // remap peer issued stateless tokens to connection `newReceiver` + private void remapPeerIssuedResetToken(QuicPacketReceiver newReceiver) { + assert newReceiver != null; + newReceiver.activeResetTokens().forEach(resetToken -> + associateStatelessResetToken(resetToken, newReceiver)); } public void draining(final QuicConnectionImpl connection) { @@ -1588,9 +1588,10 @@ public abstract sealed class QuicEndpoint implements AutoCloseable final long idleTimeout = connection.peerPtoMs() * 3; // 3 PTO connection.localConnectionIdManager().close(); - DrainingConnection draining = new DrainingConnection(connection.connectionIds(), idleTimeout); + DrainingConnection draining = new DrainingConnection(connection.connectionIds(), + connection.activeResetTokens(), idleTimeout); // we can ignore stateless reset in the draining state. - remapPeerIssuedResetToken(connection, draining); + remapPeerIssuedResetToken(draining); connection.connectionIds().forEach((id) -> connections.compute(id, (i, r) -> remapDraining(i, r, draining))); @@ -1626,8 +1627,9 @@ public abstract sealed class QuicEndpoint implements AutoCloseable final long idleTimeout = connection.peerPtoMs() * 3; // 3 PTO connection.localConnectionIdManager().close(); - var closingConnection = new ClosingConnection(connection.connectionIds(), idleTimeout, datagram); - remapPeerIssuedResetToken(connection, closingConnection); + var closingConnection = new ClosingConnection(connection.connectionIds(), + connection.activeResetTokens(), idleTimeout, datagram); + remapPeerIssuedResetToken(closingConnection); connection.connectionIds().forEach((id) -> connections.compute(id, (i, r) -> remapClosing(i, r, closingConnection))); @@ -1745,6 +1747,7 @@ public abstract sealed class QuicEndpoint implements AutoCloseable // an instance of this class) final static long NO_IDLE_TIMEOUT = 2000; final List localConnectionIds; + private final List activeResetTokens; final long maxIdleTimeMs; final long id; int more = 1; @@ -1752,7 +1755,8 @@ public abstract sealed class QuicEndpoint implements AutoCloseable volatile Deadline deadline; volatile Deadline updatedDeadline; - ClosedConnection(List localConnectionIds, long maxIdleTimeMs) { + ClosedConnection(List localConnectionIds, List activeResetTokens, long maxIdleTimeMs) { + this.activeResetTokens = activeResetTokens; this.id = QuicTimerQueue.newEventId(); this.maxIdleTimeMs = maxIdleTimeMs == 0 ? NO_IDLE_TIMEOUT : maxIdleTimeMs; this.deadline = Deadline.MAX; @@ -1765,6 +1769,11 @@ public abstract sealed class QuicEndpoint implements AutoCloseable return localConnectionIds; } + @Override + public List activeResetTokens() { + return activeResetTokens; + } + @Override public final void processIncoming(SocketAddress source, ByteBuffer destConnId, HeadersType headersType, ByteBuffer buffer) { Deadline updated = updatedDeadline; @@ -1868,9 +1877,9 @@ public abstract sealed class QuicEndpoint implements AutoCloseable final ByteBuffer closePacket; - ClosingConnection(List localConnIdManager, long maxIdleTimeMs, + ClosingConnection(List localConnectionIds, List activeResetTokens, long maxIdleTimeMs, ByteBuffer closePacket) { - super(localConnIdManager, maxIdleTimeMs); + super(localConnectionIds, activeResetTokens, maxIdleTimeMs); this.closePacket = Objects.requireNonNull(closePacket); } @@ -1903,8 +1912,8 @@ public abstract sealed class QuicEndpoint implements AutoCloseable */ final class DrainingConnection extends ClosedConnection { - DrainingConnection(List localConnIdManager, long maxIdleTimeMs) { - super(localConnIdManager, maxIdleTimeMs); + DrainingConnection(List localConnectionIds, List activeResetTokens, long maxIdleTimeMs) { + super(localConnectionIds, activeResetTokens, maxIdleTimeMs); } @Override diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicPacketReceiver.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicPacketReceiver.java index 6652b44de3a..72f622731f3 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicPacketReceiver.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicPacketReceiver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -51,6 +51,11 @@ public interface QuicPacketReceiver { */ List connectionIds(); + /** + * {@return a list of active peer stateless reset tokens for this connection) + */ + List activeResetTokens(); + /** * {@return the initial connection id assigned by the peer} * On the client side, this is always {@link Optional#empty()}. diff --git a/src/java.security.jgss/windows/man/kinit.md b/src/java.security.jgss/windows/man/kinit.md index 130bf7b007a..a32d6634e74 100644 --- a/src/java.security.jgss/windows/man/kinit.md +++ b/src/java.security.jgss/windows/man/kinit.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2026, 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 @@ -79,34 +79,34 @@ will compromise your password. You can specify one of the following commands. After the command, specify the options for it. -`-A` +[`-A`]{#option-A} : Doesn't include addresses. -`-f` +[`-f`]{#option-f} : Issues a forwardable ticket. -`-p` +[`-p`]{#option-p} : Issues a proxiable ticket. -`-c` *cache\_name* +[`-c`]{#option-c} *cache\_name* : The cache name (for example, `FILE:D:\temp\mykrb5cc`). -`-l` *lifetime* +[`-l`]{#option-l} *lifetime* : Sets the lifetime of a ticket. The value can be one of "h:m[:s]", "NdNhNmNs", and "N". See the [MIT krb5 Time Duration definition]( http://web.mit.edu/kerberos/krb5-1.17/doc/basic/date_format.html#duration) for more information. -`-r` *renewable\_time* +[`-r`]{#option-r} *renewable\_time* : Sets the total lifetime that a ticket can be renewed. -`-R` +[`-R`]{#option-R} : Renews a ticket. -`-k` +[`-k`]{#option-k} : Uses keytab -`-t` *keytab\_filename* +[`-t`]{#option-t} *keytab\_filename* : The keytab name (for example, `D:\winnt\profiles\duke\krb5.keytab`). *principal* diff --git a/src/java.security.jgss/windows/man/klist.md b/src/java.security.jgss/windows/man/klist.md index 67c84e65dec..c4874f7dc67 100644 --- a/src/java.security.jgss/windows/man/klist.md +++ b/src/java.security.jgss/windows/man/klist.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2026, 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 @@ -45,7 +45,7 @@ the contents of the credentials cache or keytab using the `klist` tool. The ## Commands -`-c` +[`-c`]{#option-c} : Specifies that the credential cache is to be listed. The following are the options for credential cache entries: @@ -62,7 +62,7 @@ the contents of the credentials cache or keytab using the `klist` tool. The `-n` : If the `-a` option is specified, don't reverse resolve addresses. -`-k` +[`-k`]{#option-k} : Specifies that key tab is to be listed. List the keytab entries. The following are the options for keytab entries: diff --git a/src/java.security.jgss/windows/man/ktab.md b/src/java.security.jgss/windows/man/ktab.md index d6817ab81cf..36e09d56e3e 100644 --- a/src/java.security.jgss/windows/man/ktab.md +++ b/src/java.security.jgss/windows/man/ktab.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2026, 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 @@ -72,12 +72,12 @@ send a keytab file over a network in the clear. ## Commands and Options -`-l` \[`-e`\] \[`-t`\] +[`-l`]{#command-l} \[`-e`\] \[`-t`\] : Lists the keytab name and entries. When `-e` is specified, the encryption type for each entry is displayed. When `-t` is specified, the timestamp for each entry is displayed. -`-a` *principal\_name* \[*password*\] \[`-n` *kvno*\] \[`-s` *salt* \| `-f`\] \[`-append`\] +[`-a`]{#command-a} *principal\_name* \[*password*\] \[`-n` *kvno*\] \[`-s` *salt* \| `-f`\] \[`-append`\] : Adds new key entries to the keytab for the given principal name with an optional *password*. If a *kvno* is specified, new keys' Key Version Numbers equal to the value, otherwise, automatically incrementing the Key @@ -90,7 +90,7 @@ send a keytab file over a network in the clear. on the command line or in a script.** This tool will prompt for a password if it isn't specified. -`-d` *principal\_name* \[`-f`\] \[`-e` *etype*\] \[*kvno* \| `all`\| `old`\] +[`-d`]{#command-d} *principal\_name* \[`-f`\] \[`-e` *etype*\] \[*kvno* \| `all`\| `old`\] : Deletes key entries from the keytab for the specified principal. No changes are made to the Kerberos database. @@ -115,7 +115,7 @@ send a keytab file over a network in the clear. This option can be used with the `-l`, `-a` or `-d` commands. -`-k` *keytab name* +[`-k`]{#option-k} *keytab name* : Specifies the keytab name and path with the `FILE:` prefix. ## Examples diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index cc21113882f..5109dfed22e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2026, 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 @@ -4000,7 +4000,10 @@ public class Attr extends JCTree.Visitor { chk.checkCastable(tree.rhs.pos(), operator.type.getReturnType(), owntype); - chk.checkLossOfPrecision(tree.rhs.pos(), operand, owntype); + switch (tree.getTag()) { + case SL_ASG, SR_ASG, USR_ASG -> { } // we only use (at most) the lower 6 bits, so any integral type is OK + default -> chk.checkLossOfPrecision(tree.rhs.pos(), operand, owntype); + } chk.checkOutOfRangeShift(tree.rhs.pos(), operator, operand); } result = check(tree, owntype, KindSelector.VAL, resultInfo); @@ -4266,7 +4269,7 @@ public class Attr extends JCTree.Visitor { } List expectedRecordTypes; - if (site.tsym.kind == Kind.TYP && ((ClassSymbol) site.tsym).isRecord()) { + if (site.tsym instanceof ClassSymbol clazz && clazz.isRecord()) { ClassSymbol record = (ClassSymbol) site.tsym; expectedRecordTypes = record.getRecordComponents() .stream() diff --git a/src/jdk.compiler/share/data/symbols/java.base-Q.sym.txt b/src/jdk.compiler/share/data/symbols/java.base-Q.sym.txt index 29ecd9f0304..9166b505c51 100644 --- a/src/jdk.compiler/share/data/symbols/java.base-Q.sym.txt +++ b/src/jdk.compiler/share/data/symbols/java.base-Q.sym.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2025, 2026, 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 @@ -429,6 +429,10 @@ innerclass innerClass java/io/ObjectOutputStream$PutField outerClass java/io/Obj innerclass innerClass java/util/AbstractMap$SimpleImmutableEntry outerClass java/util/AbstractMap innerClassName SimpleImmutableEntry flags 9 innerclass innerClass java/util/concurrent/ConcurrentHashMap$CollectionView outerClass java/util/concurrent/ConcurrentHashMap innerClassName CollectionView flags 408 +class name java/util/concurrent/CopyOnWriteArraySet +header extends java/util/AbstractSet implements java/io/Serializable flags 21 signature Ljava/util/AbstractSet;Ljava/io/Serializable; +innerclass innerClass java/io/ObjectInputStream$GetField outerClass java/io/ObjectInputStream innerClassName GetField flags 409 + class name java/util/concurrent/StructuredTaskScope -method name open descriptor (Ljava/util/concurrent/StructuredTaskScope$Joiner;Ljava/util/function/Function;)Ljava/util/concurrent/StructuredTaskScope; method name open descriptor (Ljava/util/concurrent/StructuredTaskScope$Joiner;Ljava/util/function/UnaryOperator;)Ljava/util/concurrent/StructuredTaskScope; flags 9 signature (Ljava/util/concurrent/StructuredTaskScope$Joiner<-TT;+TR;>;Ljava/util/function/UnaryOperator;)Ljava/util/concurrent/StructuredTaskScope; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java index 4bf6a0305a3..0ec7f1949bd 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java @@ -113,12 +113,13 @@ public final class LinuxAMD64CFrame extends BasicCFrame { // override base class impl to avoid ELF parsing public ClosestSymbol closestSymbolToPC() { Address symAddr = use1ByteBeforeToLookup ? pc().addOffsetTo(-1) : pc(); + var sym = dbg.lookup(dbg.getAddressValue(symAddr)); // Returns a special symbol if the address is signal handler, // otherwise returns closest symbol generated by LinuxDebugger. return dbg.isSignalTrampoline(symAddr) - ? new ClosestSymbol("", 0) - : dbg.lookup(dbg.getAddressValue(symAddr)); + ? new ClosestSymbol(sym.getName() + " ", 0) + : sym; } public Address pc() { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java index 350931cd443..09bb0607759 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java @@ -182,7 +182,44 @@ abstract class AbstractVector extends Vector { final AbstractVector asVectorRawTemplate(LaneType laneType) { // NOTE: This assumes that convert0('X') // respects REGISTER_ENDIAN order. - return convert0('X', vspecies().withLanes(laneType)); + return convert0('X', vspecies().withLanes(laneType)).swapIfNeeded(vspecies()); + } + + @ForceInline + protected static VectorShuffle normalizeSubLanesForSpecies(AbstractSpecies targetSpecies, int subLanesPerSrc) { + final int lanes = targetSpecies.laneCount(); + + if ((lanes % subLanesPerSrc) != 0) { + throw new IllegalArgumentException("laneCount " + lanes + " not divisible by subLanesPerSrc " + subLanesPerSrc); + } + + // Each group corresponds to one source lane. + // For each group, reverse the lanes inside that group. + final int groups = lanes / subLanesPerSrc; + int[] map = new int[lanes]; + for (int g = 0; g < groups; ++g) { + int base = g * subLanesPerSrc; + for (int j = 0; j < subLanesPerSrc; ++j) { + map[base + j] = base + (subLanesPerSrc - 1 - j); + } + } + return VectorShuffle.fromArray(targetSpecies, map, 0); + } + + @ForceInline + protected final int subLanesToSwap(AbstractSpecies srcSpecies) { + if (java.nio.ByteOrder.nativeOrder() != ByteOrder.BIG_ENDIAN) { + return -1; + } + int sBytes = srcSpecies.elementSize(); + int tBytes = vspecies().elementSize(); + + // No lane reordering needed for same size or widening reinterprets + if (sBytes == tBytes || (sBytes % tBytes) != 0) { + return -1; + } + int subLanesPerSrc = sBytes / tBytes; + return subLanesPerSrc; } /*package-private*/ @@ -242,6 +279,9 @@ abstract class AbstractVector extends Vector { /*package-private*/ abstract AbstractVector maybeSwap(ByteOrder bo); + /*package-private*/ + abstract AbstractVector swapIfNeeded(AbstractSpecies srcSpecies); + /*package-private*/ @ForceInline VectorShuffle swapBytesShuffle() { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java index 02e15d5f8dd..c071026a3b3 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java @@ -4101,6 +4101,14 @@ public abstract class ByteVector extends AbstractVector { return this; } + /*package-private*/ + @Override + @ForceInline + final + ByteVector swapIfNeeded(AbstractSpecies srcSpecies) { + return this; + } + static final int ARRAY_SHIFT = 31 - Integer.numberOfLeadingZeros(Unsafe.ARRAY_BYTE_INDEX_SCALE); static final long ARRAY_BASE = diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java index 08fda9c96e6..e280ca4150c 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java @@ -3612,6 +3612,18 @@ public abstract class DoubleVector extends AbstractVector { return this; } + @Override + @ForceInline + final + DoubleVector swapIfNeeded(AbstractSpecies srcSpecies) { + int subLanesPerSrc = subLanesToSwap(srcSpecies); + if (subLanesPerSrc < 0) { + return this; + } + VectorShuffle shuffle = normalizeSubLanesForSpecies(this.vspecies(), subLanesPerSrc); + return (DoubleVector) this.rearrange(shuffle); + } + static final int ARRAY_SHIFT = 31 - Integer.numberOfLeadingZeros(Unsafe.ARRAY_DOUBLE_INDEX_SCALE); static final long ARRAY_BASE = diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java index 0f70a2b81c8..35a8f4a78cf 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java @@ -3562,6 +3562,18 @@ public abstract class FloatVector extends AbstractVector { return this; } + @Override + @ForceInline + final + FloatVector swapIfNeeded(AbstractSpecies srcSpecies) { + int subLanesPerSrc = subLanesToSwap(srcSpecies); + if (subLanesPerSrc < 0) { + return this; + } + VectorShuffle shuffle = normalizeSubLanesForSpecies(this.vspecies(), subLanesPerSrc); + return (FloatVector) this.rearrange(shuffle); + } + static final int ARRAY_SHIFT = 31 - Integer.numberOfLeadingZeros(Unsafe.ARRAY_FLOAT_INDEX_SCALE); static final long ARRAY_BASE = diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java index 23e703dcada..f340aed4fce 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java @@ -3720,6 +3720,18 @@ public abstract class IntVector extends AbstractVector { return this; } + @Override + @ForceInline + final + IntVector swapIfNeeded(AbstractSpecies srcSpecies) { + int subLanesPerSrc = subLanesToSwap(srcSpecies); + if (subLanesPerSrc < 0) { + return this; + } + VectorShuffle shuffle = normalizeSubLanesForSpecies(this.vspecies(), subLanesPerSrc); + return (IntVector) this.rearrange(shuffle); + } + static final int ARRAY_SHIFT = 31 - Integer.numberOfLeadingZeros(Unsafe.ARRAY_INT_INDEX_SCALE); static final long ARRAY_BASE = diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java index 58bfd4d7772..b842bdebdc4 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java @@ -3655,6 +3655,18 @@ public abstract class LongVector extends AbstractVector { return this; } + @Override + @ForceInline + final + LongVector swapIfNeeded(AbstractSpecies srcSpecies) { + int subLanesPerSrc = subLanesToSwap(srcSpecies); + if (subLanesPerSrc < 0) { + return this; + } + VectorShuffle shuffle = normalizeSubLanesForSpecies(this.vspecies(), subLanesPerSrc); + return (LongVector) this.rearrange(shuffle); + } + static final int ARRAY_SHIFT = 31 - Integer.numberOfLeadingZeros(Unsafe.ARRAY_LONG_INDEX_SCALE); static final long ARRAY_BASE = diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java index 7ab7e7c4417..8b4c9bc5a77 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java @@ -4074,6 +4074,18 @@ public abstract class ShortVector extends AbstractVector { return this; } + @Override + @ForceInline + final + ShortVector swapIfNeeded(AbstractSpecies srcSpecies) { + int subLanesPerSrc = subLanesToSwap(srcSpecies); + if (subLanesPerSrc < 0) { + return this; + } + VectorShuffle shuffle = normalizeSubLanesForSpecies(this.vspecies(), subLanesPerSrc); + return (ShortVector) this.rearrange(shuffle); + } + static final int ARRAY_SHIFT = 31 - Integer.numberOfLeadingZeros(Unsafe.ARRAY_SHORT_INDEX_SCALE); static final long ARRAY_BASE = diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/Validator.java b/src/jdk.jartool/share/classes/sun/tools/jar/Validator.java index be694eeb1bc..c6f6f012543 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/Validator.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/Validator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -47,6 +47,8 @@ import java.util.function.Function; import java.util.function.IntSupplier; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.jar.Attributes; +import java.util.jar.Manifest; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; @@ -100,6 +102,7 @@ final class Validator { this.zf = zf; this.zis = zis; checkModuleDescriptor(MODULE_INFO); + checkAutomaticModuleName(); } static boolean validate(Main main, File zipFile) throws IOException { @@ -453,6 +456,36 @@ final class Validator { }); } + /** + * Checks whether an Automatic-Module-Name entry is valid + * and also verifies it to the name given by a compiled + * module descriptor. + */ + private void checkAutomaticModuleName() { + var entry = zf.getEntry("META-INF/MANIFEST.MF"); + if (entry == null) { + return; + } + try (InputStream jis = zf.getInputStream(entry)) { + Attributes attributes = new Manifest(jis).getMainAttributes(); + String automaticModuleName = attributes.getValue("Automatic-Module-Name"); + if (automaticModuleName == null) { + return; + } + try { + ModuleDescriptor.newAutomaticModule(automaticModuleName); + } catch (IllegalArgumentException e) { + errorAndInvalid(formatMsg("error.validator.manifest.invalid.automatic.module.name", automaticModuleName)); + } + if (md == null || automaticModuleName.equals(md.name())) { + return; + } + errorAndInvalid(formatMsg("error.validator.manifest.inconsistent.automatic.module.name", automaticModuleName, md.name())); + } catch (IOException e) { + errorAndInvalid(e.getMessage()); + } + } + /* * Checks whether or not the given versioned module descriptor's attributes * are valid when compared against the root/base module descriptor. diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties index c80377e20c1..95c5f80197d 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties +++ b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties @@ -138,6 +138,10 @@ error.validator.metainf.wrong.position=\ expected entry META-INF/ to be at position 0, but found: {0} error.validator.manifest.wrong.position=\ expected entry META-INF/MANIFEST.MF to be at position 0 or 1, but found it at position: {0} +error.validator.manifest.invalid.automatic.module.name=\ + invalid module name of Automatic-Module-Name entry in manifest: {0} +error.validator.manifest.inconsistent.automatic.module.name=\ + expected Automatic-Module-Name entry in manifest: {0} to match name of compiled module: {1} warn.validator.identical.entry=\ Warning: entry {0} contains a class that\n\ is identical to an entry already in the jar diff --git a/src/jdk.jartool/share/man/jarsigner.md b/src/jdk.jartool/share/man/jarsigner.md index 0ecc159a037..d128b9c11ff 100644 --- a/src/jdk.jartool/share/man/jarsigner.md +++ b/src/jdk.jartool/share/man/jarsigner.md @@ -483,7 +483,7 @@ the following standards: `-keystore` option is relevant for signing and verifying a JAR file. In addition, aliases are specified when signing and verifying a JAR file. -`-keystore` *url* +[`-keystore`]{#option-keystore} *url* : Specifies the URL that tells the keystore location. This defaults to the file `.keystore` in the user's home directory, as determined by the `user.home` system property. @@ -517,7 +517,7 @@ the following standards: > `keytool -keystore NONE -storetype PKCS11 -list` -`-storepass` \[`:env` \| `:file`\] *argument* +[`-storepass`]{#option-storepass} \[`:env` \| `:file`\] *argument* : Specifies the password that is required to access the keystore. This is only needed when signing (not verifying) a JAR file. In that case, if a `-storepass` option isn't provided at the command line, then the user is @@ -536,7 +536,7 @@ the following standards: The password shouldn't be specified on the command line or in a script unless it is for testing purposes, or you are on a secure system. -`-storetype` *storetype* +[`-storetype`]{#option-storetype} *storetype* : Specifies the type of keystore to be instantiated. The default keystore type is the one that is specified as the value of the `keystore.type` property in the security properties file, which is returned by the static @@ -548,7 +548,7 @@ the following standards: (such as a dedicated PIN-pad or a biometric reader), then the `-protected` option must be specified and no password options can be specified. -`-keypass` \[`:env` \| `:file`\] *argument* `-certchain` *file* +[`-keypass`]{#option-keypass} \[`:env` \| `:file`\] *argument* `-certchain` *file* : Specifies the password used to protect the private key of the keystore entry addressed by the alias specified on the command line. The password is required when using `jarsigner` to sign a JAR file. If no password is @@ -568,7 +568,7 @@ the following standards: The password shouldn't be specified on the command line or in a script unless it is for testing purposes, or you are on a secure system. -`-certchain` *file* +[`-certchain`]{#option-certchain} *file* : Specifies the certificate chain to be used when the certificate chain associated with the private key of the keystore entry that is addressed by the alias specified on the command line isn't complete. This can happen @@ -579,7 +579,7 @@ the following standards: (also known as Base64 encoding) as defined by [Internet RFC 1421 Certificate Encoding Standard](http://tools.ietf.org/html/rfc1421). -`-sigfile` *file* +[`-sigfile`]{#option-sigfile} *file* : Specifies the base file name to be used for the generated `.SF` and signature block files. For example, if file is `DUKESIGN`, then the generated `.SF` and signature block files are named `DUKESIGN.SF` and `DUKESIGN.RSA`, and placed in the @@ -598,10 +598,10 @@ the following standards: file name, then each such character is converted to an underscore (\_) character to form the file name. -`-signedjar` *file* +[`-signedjar`]{#option-signedjar} *file* : Specifies the name of signed JAR file. -`-digestalg` *algorithm* +[`-digestalg`]{#option-digestalg} *algorithm* : Specifies the name of the message digest algorithm to use when digesting the entries of a JAR file. @@ -613,7 +613,7 @@ the following standards: specified algorithm or the user must specify one with the `-addprovider` or `-providerClass` options; otherwise, the command will not succeed. -`-sigalg` *algorithm* +[`-sigalg`]{#option-sigalg} *algorithm* : Specifies the name of the signature algorithm to use to sign the JAR file. This algorithm must be compatible with the private key used to sign the @@ -627,10 +627,10 @@ the following standards: For a list of standard signature algorithm names, see the Java Security Standard Algorithm Names Specification. -`-verify` +[`-verify`]{#option-verify} : Verifies a signed JAR file. -`-verbose`\[`:`*suboptions*\] +[`-verbose`]{#option-verbose}\[`:`*suboptions*\] : When the `-verbose` option appears on the command line, it indicates that the `jarsigner` use the verbose mode when signing or verifying with the suboptions determining how much information is shown. This causes the , @@ -654,7 +654,7 @@ the following standards: more)*. See [Example of Verifying a Signed JAR File] and [Example of Verification with Certificate Information]. -`-certs` +[`-certs`]{#option-certs} : If the `-certs` option appears on the command line with the `-verify` and `-verbose` options, then the output includes certificate information for each signer of the JAR file. This information includes the name of the type @@ -669,14 +669,14 @@ the following standards: the alias name for the keystore entry for that signer is displayed in parentheses. -`-revCheck` +[`-revCheck`]{#option-revCheck} : This option enables revocation checking of certificates when signing or verifying a JAR file. The `jarsigner` command attempts to make network connections to fetch OCSP responses and CRLs if the `-revCheck` option is specified on the command line. Note that revocation checks are not enabled unless this option is specified. -`-tsa` *url* +[`-tsa`]{#option-tsa} *url* : If `-tsa http://example.tsa.url` appears on the command line when signing a JAR file then a time stamp is generated for the signature. The URL, `http://example.tsa.url`, identifies the location of the Time Stamping @@ -689,7 +689,7 @@ the following standards: stamp token returned by the TSA is stored with the signature in the signature block file. -`-tsacert` *alias* +[`-tsacert`]{#option-tsacert} *alias* : When `-tsacert` *alias* appears on the command line when signing a JAR file, a time stamp is generated for the signature. The alias identifies the TSA public key certificate in the keystore that is in effect. The entry's @@ -699,7 +699,7 @@ the following standards: The TSA public key certificate must be present in the keystore when using the `-tsacert` option. -`-tsapolicyid` *policyid* +[`-tsapolicyid`]{#option-tsapolicyid} *policyid* : Specifies the object identifier (OID) that identifies the policy ID to be sent to the TSA server. If this option isn't specified, no policy ID is sent and the TSA server will choose a default policy ID. @@ -708,7 +708,7 @@ the following standards: Standardization Sector (ITU-T) standard. These identifiers are typically period-separated sets of non-negative digits like `1.2.3.4`, for example. -`-tsadigestalg` *algorithm* +[`-tsadigestalg`]{#option-tsadigestalg} *algorithm* : Specifies the message digest algorithm that is used to generate the message imprint to be sent to the TSA server. If this option isn't specified, SHA-384 will be used. @@ -718,7 +718,7 @@ the following standards: For a list of standard message digest algorithm names, see the Java Security Standard Algorithm Names Specification. -`-internalsf` +[`-internalsf`]{#option-internalsf} : In the past, the signature block file generated when a JAR file was signed included a complete encoded copy of the `.SF` file (signature file) also generated. This behavior has been changed. To reduce the overall @@ -728,7 +728,7 @@ the following standards: In practice, don't use the `-internalsf` option because it incurs higher overhead. -`-sectionsonly` +[`-sectionsonly`]{#option-sectionsonly} : If the `-sectionsonly` option appears on the command line, then the `.SF` file (signature file) generated when a JAR file is signed doesn't include a header that contains a hash of the whole manifest file. It contains only @@ -747,12 +747,12 @@ the following standards: The `-sectionsonly` option is primarily used for testing. It shouldn't be used other than for testing because using it incurs higher overhead. -`-protected` +[`-protected`]{#option-protected} : Values can be either `true` or `false`. Specify `true` when a password must be specified through a protected authentication path such as a dedicated PIN reader. -`-providerName` *providerName* +[`-providerName`]{#option-providerName} *providerName* : If more than one provider was configured in the `java.security` security properties file, then you can use the `-providerName` option to target a specific provider instance. The argument to this option is the name of the @@ -768,7 +768,7 @@ the following standards: > `jarsigner -keystore NONE -storetype PKCS11 -providerName SunPKCS11-SmartCard -list` -`-addprovider` *name* \[`-providerArg` *arg*\] +[`-addprovider`]{#option-addprovider} *name* \[`-providerArg` *arg*\] : Adds a security provider by name (such as SunPKCS11) and an optional configure argument. The value of the security provider is the name of a security provider that is defined in a module. @@ -782,7 +782,7 @@ the following standards: > `jarsigner -keystore NONE -storetype PKCS11 -addprovider SunPKCS11 -providerArg /mydir1/mydir2/token.config` -`-providerClass` *provider-class-name* \[`-providerArg` *arg*\] +[`-providerClass`]{#option-providerClass} *provider-class-name* \[`-providerArg` *arg*\] : Used to specify the name of cryptographic service provider's master class file when the service provider isn't listed in the `java.security` security properties file. Adds a security provider by fully-qualified class name and @@ -792,7 +792,7 @@ the following standards: The preferred way to load PKCS11 is by using modules. See `-addprovider`. -`-providerPath` *classpath* +[`-providerPath`]{#option-providerPath} *classpath* : Used to specify the classpath for providers specified by the `-providerClass` option. Multiple paths should be separated by the system-dependent path-separator character. @@ -804,13 +804,13 @@ the following standards: execution environment or memory usage. For a list of possible interpreter options, type `java -h` or `java -X` at the command line. -`-strict` +[`-strict`]{#option-strict} : During the signing or verifying process, the command may issue warning messages. If you specify this option, the exit code of the tool reflects the severe warning messages that this command found. See [Errors and Warnings]. -`-conf` *url* +[`-conf`]{#option-conf} *url* : Specifies a pre-configured options file. Read the [keytool documentation](keytool.html#pre-configured-options-file) for details. The property keys supported are "jarsigner.all" for all actions, @@ -818,7 +818,7 @@ the following standards: `jarsigner` arguments including the JAR file name and alias name(s) cannot be set in this file. -`-version` +[`-version`]{#option-version} : Prints the program version. ## Errors and Warnings diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css index 53b9d8ca6b2..6198df5c2f3 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css @@ -163,6 +163,13 @@ \ '); --current-theme-svg: url("moon.svg"); + div.block a[href], div.horizontal-scroll a[href], div.inherited-list h3 a[href], header > div a[href], + .hierarchy a[href], .index a[href], div.caption a[href], .sub-title a[href], .deprecation-comment a[href], + .serialized-class-details a[href] { + text-decoration: underline; + text-decoration-thickness: 0.02em; + text-underline-offset: 0.12em; + } } /* * Styles for individual HTML elements. diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java index 825672cfe6d..3baae08eed6 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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,8 @@ public class JlinkTask { * Read the release.txt from the module. */ private static Optional getReleaseInfo(ModuleReference mref) { - try { - Optional release = mref.open().open(JDK_RELEASE_RESOURCE); + try (var moduleReader = mref.open()) { + Optional release = moduleReader.open(JDK_RELEASE_RESOURCE); if (release.isEmpty()) { return Optional.empty(); diff --git a/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java b/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java index d5b44b569a4..1cf0c85702f 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -43,7 +43,6 @@ import com.sun.source.tree.NewClassTree; import com.sun.source.tree.Scope; import com.sun.source.tree.Tree; import com.sun.source.tree.Tree.Kind; -import static com.sun.source.tree.Tree.Kind.METHOD; import com.sun.source.tree.TypeParameterTree; import com.sun.source.tree.VariableTree; import com.sun.source.tree.YieldTree; @@ -81,6 +80,7 @@ import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.function.Predicate; +import java.util.TreeSet; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; @@ -114,7 +114,6 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; import java.util.Set; -import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.BiConsumer; @@ -149,14 +148,10 @@ import javax.tools.JavaFileManager.Location; import javax.tools.StandardLocation; import jdk.jshell.ExpressionToTypeInfo.ExpressionInfo; -import static jdk.jshell.Util.REPL_DOESNOTMATTER_CLASS_NAME; import static jdk.jshell.SourceCodeAnalysis.Completeness.DEFINITELY_INCOMPLETE; import static jdk.jshell.TreeDissector.printType; import static java.util.stream.Collectors.joining; -import static javax.lang.model.element.ElementKind.CONSTRUCTOR; -import static javax.lang.model.element.ElementKind.MODULE; -import static javax.lang.model.element.ElementKind.PACKAGE; import javax.lang.model.type.IntersectionType; import javax.lang.model.util.Elements; @@ -815,7 +810,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis { }; String wrappedCode = codeWrap.wrapped(); return this.proc.taskFactory.analyze(codeWrap, task -> { - List result = new ArrayList<>(); + TreeSet result = new TreeSet<>(Comparator.comparing(Highlight::start).thenComparing(Highlight::end)); CompilationUnitTree cut = task.cuTrees().iterator().next(); Trees trees = task.trees(); SourcePositions sp = trees.getSourcePositions(); @@ -1050,8 +1045,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis { } }.scan(cut, null); result.removeIf(h -> h.start() == h.end()); - Collections.sort(result, (h1, h2) -> h1.start() - h2.start()); - return result; + return new ArrayList<>(result); }); } diff --git a/test/docs/ProblemList.txt b/test/docs/ProblemList.txt index c846678665d..d856f9c955e 100644 --- a/test/docs/ProblemList.txt +++ b/test/docs/ProblemList.txt @@ -1,6 +1,6 @@ ########################################################################### # -# Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2024, 2026, 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,3 +39,16 @@ # More than one label is allowed but must be on the same line. # ############################################################################# + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/hotspot/gtest/gc/g1/test_g1CardSetContainers.cpp b/test/hotspot/gtest/gc/g1/test_g1CardSetContainers.cpp index d83d8ffeef6..91951b35405 100644 --- a/test/hotspot/gtest/gc/g1/test_g1CardSetContainers.cpp +++ b/test/hotspot/gtest/gc/g1/test_g1CardSetContainers.cpp @@ -25,6 +25,7 @@ #include "gc/g1/g1HeapRegionBounds.inline.hpp" #include "gc/shared/cardTable.hpp" #include "memory/allocation.inline.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/powerOfTwo.hpp" #include "unittest.hpp" @@ -82,48 +83,48 @@ void G1CardSetContainersTest::cardset_inlineptr_test(uint bits_per_card) { G1AddCardResult res; - G1CardSet::ContainerPtr value = G1CardSetInlinePtr(); + Atomic value{}; for (uint i = 0; i < CardsPerSet; i++) { { - G1CardSetInlinePtr cards(&value, value); + G1CardSetInlinePtr cards(&value, value.load_relaxed()); res = cards.add(i + 1, bits_per_card, CardsPerSet); ASSERT_TRUE(res == Added); } { - G1CardSetInlinePtr cards(&value, value); + G1CardSetInlinePtr cards(&value, value.load_relaxed()); ASSERT_TRUE(cards.contains(i + 1, bits_per_card)); } } for (uint i = 0; i < CardsPerSet; i++) { - G1CardSetInlinePtr cards(value); + G1CardSetInlinePtr cards(value.load_relaxed()); ASSERT_TRUE(cards.contains(i + 1, bits_per_card)); } // Try to add again, should all return that the card had been added. for (uint i = 0; i < CardsPerSet; i++) { - G1CardSetInlinePtr cards(&value, value); + G1CardSetInlinePtr cards(&value, value.load_relaxed()); res = cards.add(i + 1, bits_per_card, CardsPerSet); ASSERT_TRUE(res == Found); } // Should be no more space in set. { - G1CardSetInlinePtr cards(&value, value); + G1CardSetInlinePtr cards(&value, value.load_relaxed()); res = cards.add(CardsPerSet + 1, bits_per_card, CardsPerSet); ASSERT_TRUE(res == Overflow); } // Cards should still be in the set. for (uint i = 0; i < CardsPerSet; i++) { - G1CardSetInlinePtr cards(value); + G1CardSetInlinePtr cards(value.load_relaxed()); ASSERT_TRUE(cards.contains(i + 1, bits_per_card)); } // Boundary cards should not be in the set. { - G1CardSetInlinePtr cards(value); + G1CardSetInlinePtr cards(value.load_relaxed()); ASSERT_TRUE(!cards.contains(0, bits_per_card)); ASSERT_TRUE(!cards.contains(CardsPerSet + 1, bits_per_card)); } @@ -131,7 +132,7 @@ void G1CardSetContainersTest::cardset_inlineptr_test(uint bits_per_card) { // Verify iteration finds all cards too and only those. { G1FindCardsInRange found(1, CardsPerSet); - G1CardSetInlinePtr cards(value); + G1CardSetInlinePtr cards(value.load_relaxed()); cards.iterate(found, bits_per_card); found.verify_all_found(); } diff --git a/test/hotspot/gtest/gc/z/test_zForwarding.cpp b/test/hotspot/gtest/gc/z/test_zForwarding.cpp index 5275b78fb82..6b66d43ae7f 100644 --- a/test/hotspot/gtest/gc/z/test_zForwarding.cpp +++ b/test/hotspot/gtest/gc/z/test_zForwarding.cpp @@ -44,11 +44,12 @@ using namespace testing; class ZForwardingTest : public ZTest { public: // Setup and tear down - ZHeap* _old_heap; - ZGenerationOld* _old_old; - ZGenerationYoung* _old_young; - ZAddressReserver _zaddress_reserver; - zoffset _page_offset; + ZHeap* _old_heap; + ZGenerationOld* _old_old; + ZGenerationYoung* _old_young; + ZAddressReserver _zaddress_reserver; + ZPhysicalMemoryBackingMocker _physical_backing; + zoffset _page_offset; virtual void SetUp() { _old_heap = ZHeap::_heap; @@ -73,8 +74,16 @@ public: GTEST_SKIP() << "Unable to reserve memory"; } - char* const addr = (char*)untype(ZOffset::address_unsafe(_page_offset)); - os::commit_memory(addr, ZGranuleSize, /* executable */ false); + // Setup backing storage + _physical_backing.SetUp(ZGranuleSize); + + size_t committed = _physical_backing()->commit(zbacking_offset(0), ZGranuleSize, 0); + + if (committed != ZGranuleSize) { + GTEST_SKIP() << "Unable to commit memory"; + } + + _physical_backing()->map(ZOffset::address_unsafe(_page_offset), ZGranuleSize, zbacking_offset(0)); } virtual void TearDown() { @@ -84,10 +93,12 @@ public: ZGeneration::_young = _old_young; if (_page_offset != zoffset::invalid) { - char* const addr = (char*)untype(ZOffset::address_unsafe(_page_offset)); - os::uncommit_memory(addr, ZGranuleSize, false /* executable */); + _physical_backing()->unmap(ZOffset::address_unsafe(_page_offset), ZGranuleSize); + _physical_backing()->uncommit(zbacking_offset(0), ZGranuleSize); } + _physical_backing.TearDown(); + _zaddress_reserver.TearDown(); } diff --git a/test/hotspot/gtest/gc/z/zunittest.hpp b/test/hotspot/gtest/gc/z/zunittest.hpp index 2464f821380..13a8eb10f8a 100644 --- a/test/hotspot/gtest/gc/z/zunittest.hpp +++ b/test/hotspot/gtest/gc/z/zunittest.hpp @@ -28,6 +28,7 @@ #include "gc/z/zArguments.hpp" #include "gc/z/zInitialize.hpp" #include "gc/z/zNUMA.hpp" +#include "gc/z/zPhysicalMemoryManager.hpp" #include "gc/z/zRangeRegistry.hpp" #include "gc/z/zVirtualMemory.inline.hpp" #include "gc/z/zVirtualMemoryManager.hpp" @@ -104,6 +105,54 @@ public: } }; + class ZPhysicalMemoryBackingMocker { + size_t _old_max; + ZPhysicalMemoryBacking* _backing; + bool _active; + + static size_t set_max(size_t max_capacity) { + size_t old_max = ZBackingOffsetMax; + + ZBackingOffsetMax = max_capacity; + ZBackingIndexMax = checked_cast(ZBackingOffsetMax >> ZGranuleSizeShift); + + return old_max; + } + + public: + ZPhysicalMemoryBackingMocker() + : _old_max(0), + _backing(nullptr), + _active(false) {} + + void SetUp(size_t max_capacity) { + GTEST_EXPECT_FALSE(_active) << "SetUp called twice without a TearDown"; + + _old_max = set_max(max_capacity); + + char* const mem = (char*)os::malloc(sizeof(ZPhysicalMemoryBacking), mtTest); + _backing = new (mem) ZPhysicalMemoryBacking(ZGranuleSize); + + _active = true; + } + + void TearDown() { + GTEST_EXPECT_TRUE(_active) << "TearDown called without a preceding SetUp"; + + _active = false; + + _backing->~ZPhysicalMemoryBacking(); + os::free(_backing); + _backing = nullptr; + + set_max(_old_max); + } + + ZPhysicalMemoryBacking* operator()() { + return _backing; + } + }; + private: ZAddressOffsetMaxSetter _zaddress_offset_max_setter; unsigned int _rand_seed; diff --git a/test/hotspot/gtest/metaspace/test_blocktree.cpp b/test/hotspot/gtest/metaspace/test_blocktree.cpp index 6d1e8d2884d..3bcd79c98ad 100644 --- a/test/hotspot/gtest/metaspace/test_blocktree.cpp +++ b/test/hotspot/gtest/metaspace/test_blocktree.cpp @@ -74,7 +74,7 @@ TEST_VM(metaspace, BlockTree_basic) { MetaWord* p = nullptr; MetaWord arr[10000]; - ASSERT_LE(BlockTree::MinWordSize, (size_t)6); // Sanity check. Adjust if Node is changed. + ASSERT_LE(BlockTree::MinWordSize, (size_t)7); // Sanity check. Adjust if Node is changed. const size_t minws = BlockTree::MinWordSize; diff --git a/test/hotspot/gtest/utilities/test_growableArray.cpp b/test/hotspot/gtest/utilities/test_growableArray.cpp index 45fc9498d27..6958da18ac3 100644 --- a/test/hotspot/gtest/utilities/test_growableArray.cpp +++ b/test/hotspot/gtest/utilities/test_growableArray.cpp @@ -232,12 +232,111 @@ protected: } } + template + static void test_remove_range(ArrayClass* a) { + // Seed initial + for (int i = 0; i < 10; i++) { + a->append(i); + } + ASSERT_EQ(a->length(), 10); + + // Remove empty range from the non-empty list, should not modify the list. + a->remove_range(0, 0); + ASSERT_EQ(a->length(), 10); + + // Remove one element from head, should result in [1 ... 9] + a->remove_range(0, 1); + ASSERT_EQ(a->length(), 9); + for (int i = 0; i < a->length(); i++) { + ASSERT_EQ(a->at(i), i + 1); + } + + // Remove one element from tail, should result in [1 ... 8] + a->remove_range(8, 9); + ASSERT_EQ(a->length(), 8); + for (int i = 0; i < a->length(); i++) { + ASSERT_EQ(a->at(i), i + 1); + } + + // Remove another empty range from the non-empty list, should not modify + a->remove_range(1, 1); + ASSERT_EQ(a->length(), 8); + + // Remove some elements from the middle, should result in [1 2 7 8] + a->remove_range(2, 6); + ASSERT_EQ(a->length(), 4); + ASSERT_EQ(a->at(0), 1); + ASSERT_EQ(a->at(1), 2); + ASSERT_EQ(a->at(2), 7); + ASSERT_EQ(a->at(3), 8); + + // Remove the rest of the elements one by one + a->remove_range(0, 1); + ASSERT_EQ(a->length(), 3); + ASSERT_EQ(a->at(0), 2); + ASSERT_EQ(a->at(1), 7); + ASSERT_EQ(a->at(2), 8); + + a->remove_range(0, 1); + ASSERT_EQ(a->length(), 2); + ASSERT_EQ(a->at(0), 7); + ASSERT_EQ(a->at(1), 8); + + a->remove_range(0, 1); + ASSERT_EQ(a->length(), 1); + ASSERT_EQ(a->at(0), 8); + + a->remove_range(0, 1); + ASSERT_EQ(a->length(), 0); + + // Remove elements from empty list with empty range, should be accepted + a->remove_range(0, 0); + ASSERT_EQ(a->length(), 0); + } + + template + static void test_remove_till(ArrayClass* a) { + // Seed initial + for (int i = 0; i < 10; i++) { + a->append(i); + } + ASSERT_EQ(a->length(), 10); + + // Remove empty range from non-empty list, should work + a->remove_till(0); + ASSERT_EQ(a->length(), 10); + + // Remove one element from head, should result in [1 ... 9] + a->remove_till(1); + ASSERT_EQ(a->length(), 9); + for (int i = 0; i < a->length(); i++) { + ASSERT_EQ(a->at(i), i + 1); + } + + // Remove two elements from head, should result in [3 ... 9] + a->remove_till(2); + ASSERT_EQ(a->length(), 7); + for (int i = 0; i < a->length(); i++) { + ASSERT_EQ(a->at(i), i + 3); + } + + // Remove remaining elements, should result in [] + a->remove_till(a->length()); + ASSERT_EQ(a->length(), 0); + + // Remove empty range from empty list, should work + a->remove_till(0); + ASSERT_EQ(a->length(), 0); + } + // Supported by all GrowableArrays enum TestEnum { Append, Clear, Capacity, - Iterator + Iterator, + RemoveRange, + RemoveTill }; template @@ -259,6 +358,14 @@ protected: test_iterator(a); break; + case RemoveRange: + test_remove_range(a); + break; + + case RemoveTill: + test_remove_till(a); + break; + default: fatal("Missing dispatch"); break; @@ -451,6 +558,14 @@ TEST_VM_F(GrowableArrayTest, iterator) { with_all_types_all_0(Iterator); } +TEST_VM_F(GrowableArrayTest, remove_range) { + with_all_types_all_0(RemoveRange); +} + +TEST_VM_F(GrowableArrayTest, remove_till) { + with_all_types_all_0(RemoveTill); +} + TEST_VM_F(GrowableArrayTest, copy) { with_no_cheap_array_append1(Copy1); } diff --git a/test/hotspot/gtest/utilities/test_metaspaceClosure.cpp b/test/hotspot/gtest/utilities/test_metaspaceClosure.cpp index b2acf45c470..c98b38b8c3c 100644 --- a/test/hotspot/gtest/utilities/test_metaspaceClosure.cpp +++ b/test/hotspot/gtest/utilities/test_metaspaceClosure.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -21,6 +21,7 @@ * questions. */ +#include "cds/aotGrowableArray.inline.hpp" #include "memory/allocation.hpp" #include "memory/metadataFactory.hpp" #include "memory/metaspaceClosure.hpp" @@ -58,7 +59,7 @@ public: class MyUniqueMetaspaceClosure : public MetaspaceClosure { static constexpr int SIZE = 10; - MyMetaData* _visited[SIZE]; + void* _visited[SIZE]; int _count; public: MyUniqueMetaspaceClosure() { @@ -71,11 +72,22 @@ public: virtual bool do_ref(Ref* ref, bool read_only) { MyMetaData* ptr = (MyMetaData*)ref->obj(); assert(_count < SIZE, "out of bounds"); - _visited[_count++] = ptr; + for (int i = 0; i < _count; i++) { + if (_visited[i] == (void*)ptr) { + // We have walked this before. + return false; + } + } + + // Found a new pointer. Let's walk it + _visited[_count++] = (void*)ptr; return true; // recurse } bool has_visited(MyMetaData* p) { + return has_visited((void*)p); + } + bool has_visited(void* p) { for (int i = 0; i < SIZE; i++) { if (_visited[i] == p) { return true; @@ -83,6 +95,9 @@ public: } return false; } + int visited_count() { + return _count; + } }; // iterate an Array @@ -104,7 +119,9 @@ TEST_VM(MetaspaceClosure, MSOPointerArrayRef) { MyUniqueMetaspaceClosure closure; closure.push(&array); + closure.finish(); + EXPECT_TRUE(closure.has_visited(array)) << "must be"; EXPECT_TRUE(closure.has_visited(&x)) << "must be"; EXPECT_TRUE(closure.has_visited(&y)) << "must be"; EXPECT_TRUE(closure.has_visited(&z)) << "must be"; @@ -130,8 +147,85 @@ TEST_VM(MetaspaceClosure, MSOArrayRef) { MyUniqueMetaspaceClosure closure; closure.push(&array); + closure.finish(); + EXPECT_TRUE(closure.has_visited(array)) << "must be"; EXPECT_TRUE(closure.has_visited(&x)) << "must be"; EXPECT_TRUE(closure.has_visited(&y)) << "must be"; EXPECT_TRUE(closure.has_visited(&z)) << "must be"; } + +// iterate an Array +TEST_VM(MetaspaceClosure, OtherArrayRef) { + JavaThread* THREAD = JavaThread::current(); + ClassLoaderData* cld = ClassLoaderData::the_null_class_loader_data(); + Array* array = MetadataFactory::new_array(cld, 4, THREAD); + + MyUniqueMetaspaceClosure closure; + closure.push(&array); + closure.finish(); + + EXPECT_TRUE(closure.has_visited(array)) << "must be"; +} + +// iterate an AOTGrowableArray +TEST_VM(MetaspaceClosure, GrowableArray_MSOPointer) { + AOTGrowableArray* array = new(mtClass) AOTGrowableArray(2, mtClass); + + MyMetaData x; + MyMetaData y; + MyMetaData z; + + array->push(&x); + array->push(&y); + y._a = &z; + + MyUniqueMetaspaceClosure closure; + closure.push(&array); + closure.finish(); + + EXPECT_TRUE(closure.has_visited(array)) << "must be"; + EXPECT_TRUE(closure.has_visited(&x)) << "must be"; + EXPECT_TRUE(closure.has_visited(&y)) << "must be"; + EXPECT_TRUE(closure.has_visited(&z)) << "must be"; +} + +// iterate an AOTGrowableArray +TEST_VM(MetaspaceClosure, GrowableArray_MSO) { + AOTGrowableArray* array = new(mtClass) AOTGrowableArray(4, mtClass); + + for (int i = 0; i < array->length(); i++) { + EXPECT_TRUE(array->at(i)._a == nullptr) << "should be initialized to null"; + EXPECT_TRUE(array->at(i)._b == nullptr) << "should be initialized to null"; + } + + MyMetaData x; + MyMetaData y; + MyMetaData z; + + z._a = &x; + z._b = &y; + y._a = &z; + array->push(z); + + MyUniqueMetaspaceClosure closure; + closure.push(&array); + closure.finish(); + + EXPECT_TRUE(closure.has_visited(array)) << "must be"; + EXPECT_TRUE(closure.has_visited(&x)) << "must be"; + EXPECT_TRUE(closure.has_visited(&y)) << "must be"; + EXPECT_TRUE(closure.has_visited(&z)) << "must be"; +} + +// iterate an AOTGrowableArray +TEST_VM(MetaspaceClosure, GrowableArray_jlong) { + AOTGrowableArray* array = new(mtClass) AOTGrowableArray(4, mtClass); + + MyUniqueMetaspaceClosure closure; + closure.push(&array); + closure.finish(); + + EXPECT_TRUE(closure.has_visited(array)) << "must be"; + EXPECT_TRUE(closure.visited_count() == 2) << "must visit buffer inside GrowableArray"; +} diff --git a/test/hotspot/jtreg/ProblemList-AotJdk.txt b/test/hotspot/jtreg/ProblemList-AotJdk.txt index e27e85645f5..0a2177b8edb 100644 --- a/test/hotspot/jtreg/ProblemList-AotJdk.txt +++ b/test/hotspot/jtreg/ProblemList-AotJdk.txt @@ -56,3 +56,16 @@ compiler/ciReplay/TestInliningProtectionDomain.java 0000000 generic-all # These tests fail often with AotJdk due to JDK-8323727 compiler/arguments/TestStressReflectiveCode.java 8323727 generic-all compiler/arraycopy/TestCloneWithStressReflectiveCode.java 8323727 generic-all + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/hotspot/jtreg/ProblemList-StaticJdk.txt b/test/hotspot/jtreg/ProblemList-StaticJdk.txt index 1e5538ca5b9..a6112169f8d 100644 --- a/test/hotspot/jtreg/ProblemList-StaticJdk.txt +++ b/test/hotspot/jtreg/ProblemList-StaticJdk.txt @@ -1,3 +1,32 @@ +# +# Copyright (c) 2025, 2026, 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. +# +# 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. +# + +############################################################################# +# +# List of quarantined tests for testing in StaticJdk mode. +# +############################################################################# + # Dynamically link with JDK/VM native libraries gtest/GTestWrapper.java 8356201 generic-all gtest/LargePageGtests.java#use-large-pages 8356201 generic-all @@ -6,3 +35,16 @@ gtest/MetaspaceGtests.java#no-ccs 8356201 generic-all gtest/NMTGtests.java#nmt-detail 8356201 generic-all gtest/NMTGtests.java#nmt-off 8356201 generic-all gtest/NMTGtests.java#nmt-summary 8356201 generic-all + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/hotspot/jtreg/ProblemList-Virtual.txt b/test/hotspot/jtreg/ProblemList-Virtual.txt index 31684662194..7c4846a9fb2 100644 --- a/test/hotspot/jtreg/ProblemList-Virtual.txt +++ b/test/hotspot/jtreg/ProblemList-Virtual.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2026, 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 @@ -77,3 +77,15 @@ vmTestbase/nsk/jdi/VMOutOfMemoryException/VMOutOfMemoryException001/VMOutOfMemor # to make progress when all other threads are currently suspended. vmTestbase/nsk/jdi/ThreadReference/isSuspended/issuspended002/TestDescription.java 8338713 generic-all +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/hotspot/jtreg/ProblemList-Xcomp.txt b/test/hotspot/jtreg/ProblemList-Xcomp.txt index 758bb32e968..e4f1774e6e0 100644 --- a/test/hotspot/jtreg/ProblemList-Xcomp.txt +++ b/test/hotspot/jtreg/ProblemList-Xcomp.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2026, 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 @@ -46,3 +46,16 @@ vmTestbase/nsk/jvmti/scenarios/capability/CM03/cm03t001/TestDescription.java 829 vmTestbase/nsk/stress/thread/thread006.java 8321476 linux-all gc/arguments/TestNewSizeFlags.java 8299116 macosx-aarch64 + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/hotspot/jtreg/ProblemList-enable-preview.txt b/test/hotspot/jtreg/ProblemList-enable-preview.txt index 31e09d372a8..1a831a0dde3 100644 --- a/test/hotspot/jtreg/ProblemList-enable-preview.txt +++ b/test/hotspot/jtreg/ProblemList-enable-preview.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2025, 2026, 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 @@ -25,6 +25,8 @@ # # List of quarantined tests for testing with --enable-preview # +# These are failures that ONLY occur with the '--enable-preview' option +# specified. There are separate sub-sections for each preview project. +# ############################################################################# - diff --git a/test/hotspot/jtreg/ProblemList-jvmti-stress-agent.txt b/test/hotspot/jtreg/ProblemList-jvmti-stress-agent.txt index eb4131691c9..1e84a39c30b 100644 --- a/test/hotspot/jtreg/ProblemList-jvmti-stress-agent.txt +++ b/test/hotspot/jtreg/ProblemList-jvmti-stress-agent.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2025, 2026, 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 @@ -96,3 +96,16 @@ serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTwoAgentsTest.java 0000000 generic-all serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorVMEventsTest.java#id0 0000000 generic-all serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorVMEventsTest.java#id1 0000000 generic-all + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/hotspot/jtreg/ProblemList-zgc.txt b/test/hotspot/jtreg/ProblemList-zgc.txt index 911e3adc9ca..3e52b7169df 100644 --- a/test/hotspot/jtreg/ProblemList-zgc.txt +++ b/test/hotspot/jtreg/ProblemList-zgc.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2019, 2026, 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 @@ -113,3 +113,16 @@ compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/NativeCallTest.java compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/SimpleCodeInstallationTest.java 8343233 generic-aarch64 compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/SimpleDebugInfoTest.java 8343233 generic-aarch64 compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/VirtualObjectDebugInfoTest.java 8343233 generic-aarch64 + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 84520b00056..3e4814180f6 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2026, 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 @@ -77,6 +77,8 @@ compiler/interpreter/Test6833129.java 8335266 generic-i586 compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64 +compiler/longcountedloops/TestLoopNestTooManyTraps.java 8376591 generic-all + ############################################################################# # :hotspot_gc @@ -181,3 +183,16 @@ vmTestbase/nsk/jdwp/ThreadReference/ForceEarlyReturn/forceEarlyReturn001/forceEa vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi005/TestDescription.java 8076494 windows-x64 vmTestbase/nsk/monitoring/ThreadMXBean/findMonitorDeadlockedThreads/find006/TestDescription.java 8310144 macosx-aarch64 + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/hotspot/jtreg/compiler/c2/TestUnlockNodeNullMemprof.java b/test/hotspot/jtreg/compiler/c2/TestUnlockNodeNullMemprof.java new file mode 100644 index 00000000000..f86dc495fd2 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestUnlockNodeNullMemprof.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025, 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. + * + * 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. + */ + +/** + * @test + * @bug 8370502 + * @summary Do not segfault while adding node to IGVN worklist + * + * @run main/othervm -Xbatch ${test.main.class} + */ + +package compiler.c2; + +public class TestUnlockNodeNullMemprof { + public static void main(String[] args) { + int[] a = new int[0]; // test only valid when size is 0. + for (int i = 0; i < Integer.valueOf(10000); i++) // test only valid with boxed loop limit + try { + test(a); + } catch (ArrayIndexOutOfBoundsException e) { + } + } + + static void test(int[] a) { + for (int i = 0; i < 1;) { + a[i] = 0; + synchronized (TestUnlockNodeNullMemprof.class) { + } + for (int j = 0; Integer.valueOf(j) < 1;) + j = 0; + } + } +} diff --git a/test/hotspot/jtreg/compiler/c2/TestVerifyLoopOptimizationsHighMemUsage.java b/test/hotspot/jtreg/compiler/c2/TestVerifyLoopOptimizationsHighMemUsage.java new file mode 100644 index 00000000000..ea7f3049ec3 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestVerifyLoopOptimizationsHighMemUsage.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2026, 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. + * + * 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. + */ + +/** + * @test + * @key stress randomness + * @bug 8370519 + * @summary C2: Hit MemLimit when running with +VerifyLoopOptimizations + * @run main/othervm -XX:CompileCommand=compileonly,${test.main.class}::* -XX:-TieredCompilation -Xbatch + * -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions + * -XX:+StressLoopPeeling -XX:+VerifyLoopOptimizations + * -XX:CompileCommand=memlimit,${test.main.class}::*,600M~crash + * -XX:StressSeed=3106998670 ${test.main.class} + * @run main ${test.main.class} + */ + +package compiler.c2; + +public class TestVerifyLoopOptimizationsHighMemUsage { + public static final int N = 400; + public static long instanceCount = -13L; + public static volatile short sFld = -16143; + public static int iFld = -159; + public static float fArrFld[] = new float[N]; + + public static long lMeth(int i1) { + int i2 = 11, i3 = 37085, i4 = 177, i5 = 190, i6 = -234, i7 = 13060, + iArr[] = new int[N]; + float f = 1.179F; + double d = 2.9685; + long lArr[] = new long[N]; + for (i2 = 15; i2 < 330; ++i2) + for (i4 = 1; i4 < 5; ++i4) { + fArrFld[i4 + 1] = (++i1); + for (i6 = 2; i6 > 1; i6 -= 3) + switch ((i2 * 5) + 54) { + case 156: + if (i4 != 0) + ; + case 168: + case 342: + case 283: + case 281: + case 328: + case 322: + case 228: + case 114: + case 207: + case 209: + case 354: + case 108: + i1 <<= i1; + case 398: + case 144: + case 218: + case 116: + case 296: + case 198: + case 173: + case 105: + case 120: + case 248: + case 140: + case 352: + try { + } catch (ArithmeticException a_e) { + } + case 404: + i5 += (i6 ^ instanceCount); + case 370: + case 211: + case 231: + try { + } catch (ArithmeticException a_e) { + } + case 251: + case 179: + f += (((i6 * sFld) + i4) - + iFld); + } + } + long meth_res = i1 + i2 + i3 + i4 + i5 + i6 + i7 + Float.floatToIntBits(f) + + Double.doubleToLongBits(d) + +checkSum(iArr) + + checkSum(lArr); + return meth_res; + } + + public static long checkSum(int[] a) { + long sum = 0; + for (int j = 0; j < a.length; j++) + sum += (a[j] / (j + 1) + a[j] % (j + 1)); + return sum; + } + + public static long checkSum(long[] a) { + long sum = 0; + for (int j = 0; j < a.length; j++) + sum += (a[j] / (j + 1) + a[j] % (j + 1)); + return sum; + } + + public static void main(String[] strArr) { + for (int i = 0; i < 10; i++) + lMeth(-159); + } +} diff --git a/test/hotspot/jtreg/compiler/c2/gvn/CmpUNodeValueTests.java b/test/hotspot/jtreg/compiler/c2/gvn/CmpUNodeValueTests.java new file mode 100644 index 00000000000..cbcbda3422f --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/gvn/CmpUNodeValueTests.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2026, 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. + * + * 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 compiler.c2.gvn; + +import compiler.lib.generators.Generators; +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; + +/* + * @test + * @bug 8375653 + * @summary Test that Value computations of CmpUNode are being performed as expected. + * @library /test/lib / + * @run driver ${test.main.class} + */ +public class CmpUNodeValueTests { + public static void main(String[] args) { + TestFramework.run(); + } + + @DontInline + public static int one() { + return 1; + } + + // Move to a separate method to prevent javac folding the constant comparison + @ForceInline + public static int oneInline() { + return 1; + } + + @Run(test = {"testIntControl", "testIntLT", "testIntLE", "testIntGT", "testIntGE", "testIntEQ", "testIntNE", + "testLongControl", "testLongLT", "testLongLE", "testLongGT", "testLongGE", "testLongEQ", "testLongNE"}) + public void run() { + var stream = Generators.G.longs(); + long x = stream.next(); + long y = stream.next(); + + Asserts.assertEQ(0, testIntControl(false, false)); + Asserts.assertEQ(0, testIntControl(false, true)); + Asserts.assertEQ(0, testIntControl(true, false)); + Asserts.assertEQ(1, testIntControl(true, true)); + Asserts.assertEQ(0, testIntLT((int) x)); + Asserts.assertEQ(0, testIntLE(false, false)); + Asserts.assertEQ(0, testIntLE(false, true)); + Asserts.assertEQ(0, testIntLE(true, false)); + Asserts.assertEQ(0, testIntLE(true, true)); + Asserts.assertEQ(0, testIntGT(false, false)); + Asserts.assertEQ(0, testIntGT(false, true)); + Asserts.assertEQ(0, testIntGT(true, false)); + Asserts.assertEQ(0, testIntGT(true, true)); + Asserts.assertEQ(0, testIntGE((int) x, (int) y)); + Asserts.assertEQ(0, testIntEQ()); + Asserts.assertEQ(0, testIntNE(false, false)); + Asserts.assertEQ(0, testIntNE(false, true)); + Asserts.assertEQ(0, testIntNE(true, false)); + Asserts.assertEQ(0, testIntNE(true, true)); + + Asserts.assertEQ(0, testLongControl(false, false)); + Asserts.assertEQ(0, testLongControl(false, true)); + Asserts.assertEQ(0, testLongControl(true, false)); + Asserts.assertEQ(1, testLongControl(true, true)); + Asserts.assertEQ(0, testLongLT((int) x, false)); + Asserts.assertEQ(0, testLongLT((int) x, true)); + Asserts.assertEQ(0, testLongLE(false, false)); + Asserts.assertEQ(0, testLongLE(false, true)); + Asserts.assertEQ(0, testLongLE(true, false)); + Asserts.assertEQ(0, testLongLE(true, true)); + Asserts.assertEQ(0, testLongGT(false, false)); + Asserts.assertEQ(0, testLongGT(false, true)); + Asserts.assertEQ(0, testLongGT(true, false)); + Asserts.assertEQ(0, testLongGT(true, true)); + Asserts.assertEQ(0, testLongGE((int) x, (int) y)); + Asserts.assertEQ(0, testLongEQ()); + Asserts.assertEQ(0, testLongNE(false, false)); + Asserts.assertEQ(0, testLongNE(false, true)); + Asserts.assertEQ(0, testLongNE(true, false)); + Asserts.assertEQ(0, testLongNE(true, true)); + } + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, counts = {IRNode.CMP_U, "1"}) + int testIntControl(boolean b1, boolean b2) { + int v1 = b1 ? 1 : -1; + int v2 = b2 ? 1 : 0; + return Integer.compareUnsigned(v1, v2) > 0 ? 0 : one(); + } + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + int testIntLT(int x) { + int v1 = x & Integer.MAX_VALUE; // Unset the highest bit, make v1 non-negative + int v2 = Integer.MIN_VALUE; + return Integer.compareUnsigned(v1, v2) < 0 ? 0 : one(); + } + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + int testIntLE(boolean b1, boolean b2) { + int v1 = b1 ? 2 : 0; + int v2 = b2 ? -1 : 2; + return Integer.compareUnsigned(v1, v2) <= 0 ? 0 : one(); + } + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + int testIntGT(boolean b1, boolean b2) { + int v1 = b1 ? Integer.MIN_VALUE : Integer.MAX_VALUE; + int v2 = b2 ? 0 : 2; + return Integer.compareUnsigned(v1, v2) > 0 ? 0 : one(); + } + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + int testIntGE(int x, int y) { + int v1 = x | Integer.MIN_VALUE; // Set the highest bit, make v1 negative + int v2 = y & Integer.MAX_VALUE; // Unset the highest bit, make v2 non-negative + return Integer.compareUnsigned(v1, v2) >= 0 ? 0 : one(); + } + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + int testIntEQ() { + return Integer.compareUnsigned(oneInline(), 1) == 0 ? 0 : one(); + } + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_U, IRNode.CALL}) + int testIntNE(boolean b1, boolean b2) { + int v1 = b1 ? 1 : -1; + int v2 = b2 ? 0 : 2; + return Integer.compareUnsigned(v1, v2) != 0 ? 0 : one(); + } + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, counts = {IRNode.CMP_UL, "1"}) + int testLongControl(boolean b1, boolean b2) { + long v1 = b1 ? 1 : -1; + long v2 = b2 ? 1 : 0; + return Long.compareUnsigned(v1, v2) > 0 ? 0 : one(); + } + + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + int testLongLT(int x, boolean b2) { + long v1 = Integer.toUnsignedLong(x); + long v2 = Integer.MIN_VALUE; + return Long.compareUnsigned(v1, v2) < 0 ? 0 : one(); + } + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + int testLongLE(boolean b1, boolean b2) { + long v1 = b1 ? 2 : 0; + long v2 = b2 ? -1 : 2; + return Long.compareUnsigned(v1, v2) <= 0 ? 0 : one(); + } + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + int testLongGT(boolean b1, boolean b2) { + long v1 = b1 ? Long.MIN_VALUE : Long.MAX_VALUE; + long v2 = b2 ? 0 : 2; + return Long.compareUnsigned(v1, v2) > 0 ? 0 : one(); + } + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + int testLongGE(int x, int y) { + long v1 = x | Long.MIN_VALUE; // Set the highest bit, make v1 negative + long v2 = y & Long.MAX_VALUE; // Unset the highest bit, make v2 non-negative + return Long.compareUnsigned(v1, v2) >= 0 ? 0 : one(); + } + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + int testLongEQ() { + return Long.compareUnsigned(oneInline(), 1L) == 0 ? 0 : one(); + } + + @Test + @IR(applyIfPlatformOr = {"x64", "true", "aarch64", "true"}, failOn = {IRNode.CMP_UL, IRNode.CALL}) + int testLongNE(boolean b1, boolean b2) { + long v1 = b1 ? 1 : -1; + long v2 = b2 ? 0 : 2; + return Long.compareUnsigned(v1, v2) != 0 ? 0 : one(); + } +} diff --git a/test/hotspot/jtreg/compiler/ccp/TestCmpUMonotonicity.java b/test/hotspot/jtreg/compiler/ccp/TestCmpUMonotonicity.java new file mode 100644 index 00000000000..5ebdd9469fc --- /dev/null +++ b/test/hotspot/jtreg/compiler/ccp/TestCmpUMonotonicity.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2026, 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. + * + * 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 compiler.ccp; + +/* + * @test + * @bug 8375653 + * @summary Test that CmpUNode::sub conforms monotonicity + * + * @run main ${test.main.class} + * @run main/othervm -Xbatch -XX:CompileCommand=compileonly,${test.main.class}::test* ${test.main.class} + */ +public class TestCmpUMonotonicity { + public static void main(String[] args) { + for (int i = 0; i < 20000; i++) { + testInt(true, 1, 100, 2); + testInt(false, 1, 100, 2); + testLong(true, 1, 100, 2); + testLong(false, 1, 100, 2); + } + } + + private static int testInt(boolean b, int start, int limit, int step) { + int v2 = b ? 1 : -1; + int v1 = 0; + for (int i = start; i < limit; i *= step) { + if (Integer.compareUnsigned(v1, v2) < 0) { + v1 = 2; + } else { + v1 = 0; + } + } + return v1; + } + + private static long testLong(boolean b, int start, int limit, int step) { + long v2 = b ? 1 : -1; + long v1 = 0; + for (int i = start; i < limit; i *= step) { + if (Long.compareUnsigned(v1, v2) < 0) { + v1 = 2; + } else { + v1 = 0; + } + } + return v1; + } +} diff --git a/test/hotspot/jtreg/compiler/corelibs/OptionalFold.java b/test/hotspot/jtreg/compiler/corelibs/OptionalFold.java new file mode 100644 index 00000000000..1f850aaf7cd --- /dev/null +++ b/test/hotspot/jtreg/compiler/corelibs/OptionalFold.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025, 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. + * + * 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 compiler.corelibs; + +import java.util.Optional; + +import compiler.lib.ir_framework.Check; +import compiler.lib.ir_framework.IR; +import compiler.lib.ir_framework.IRNode; +import compiler.lib.ir_framework.Test; +import compiler.lib.ir_framework.TestFramework; + +/* + * @test + * @bug 8372696 + * @summary Verify constant folding for Optional, both present and absent + * @library /test/lib / + * @run driver ${test.main.class} + */ +public class OptionalFold { + + public static void main(String[] args) { + // Somehow fails with -XX:-TieredCompilation + TestFramework.runWithFlags("-XX:+TieredCompilation"); + } + + // Ensure both present and empty values can fold + static final Optional ONE = Optional.of(5), TWO = Optional.empty(); + + @Test + @IR(failOn = {IRNode.ADD_I}) + public int testSum() { + return ONE.orElse(7) + TWO.orElse(12); + } + + @Check(test = "testSum") + public void checkTestSum(int res) { + if (res != 5 + 12) { + throw new RuntimeException("incorrect result: " + res); + } + } + +} diff --git a/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java b/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java index d358359ff14..5b998caf65c 100644 --- a/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java +++ b/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 IBM Corporation. All rights reserved. + * Copyright (c) 2025, 2026 IBM Corporation. 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 @@ -122,8 +122,21 @@ public class TestMinMaxIdentity { let("arg2", arg2), """ @Test - @IR(counts = {IRNode.#op, "= 1"}, - phase = CompilePhase.BEFORE_MACRO_EXPANSION) + """, + type.isFloating() ? + """ + @IR(counts = {IRNode.#op, "= 1"}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(counts = {IRNode.#op, "= 1"}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION, + applyIfPlatform = {"riscv64", "true"}) + """ : + """ + @IR(counts = {IRNode.#op, "= 1"}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION) + """, + """ @Arguments(values = {Argument.NUMBER_42, Argument.NUMBER_42}) public #type $test(#type #arg1, #type #arg2) { int i; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/CompilePhase.java b/test/hotspot/jtreg/compiler/lib/ir_framework/CompilePhase.java index a536808d269..1e3ba7c314e 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/CompilePhase.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/CompilePhase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -161,7 +161,7 @@ public enum CompilePhase { static { for (CompilePhase phase : CompilePhase.values()) { if (phase == PRINT_IDEAL) { - PHASES_BY_PARSED_NAME.put("print_ideal", phase); + PHASES_BY_PARSED_NAME.put("PrintIdeal", phase); } else { PHASES_BY_PARSED_NAME.put(phase.name(), phase); } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java index 1fea5da52ef..137efd18136 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java @@ -183,7 +183,7 @@ public class TestFramework { private List flags; private int defaultWarmup = -1; private boolean testClassesOnBootClassPath; - private boolean isAllowNotCompilable = false; + private boolean allowNotCompilable = false; /* * Public interface methods @@ -498,7 +498,7 @@ public class TestFramework { * and else it is ignored silently. */ public TestFramework allowNotCompilable() { - this.isAllowNotCompilable = true; + this.allowNotCompilable = true; return this; } @@ -721,7 +721,7 @@ public class TestFramework { nonWhiteListedFlags.forEach((f) -> System.out.println(" - " + f)); } - System.out.println(""); + System.out.println(); } /** @@ -860,8 +860,8 @@ public class TestFramework { private List anyNonWhitelistedJTregVMAndJavaOptsFlags() { List flags = Arrays.stream(Utils.getTestJavaOpts()) .map(s -> s.replaceFirst("-XX:[+|-]?|-(?=[^D|^e])", "")) - .collect(Collectors.toList()); - List nonWhiteListedFlags = new ArrayList(); + .toList(); + List nonWhiteListedFlags = new ArrayList<>(); for (String flag : flags) { if (flag.contains("agentpath")) { throw new SkippedException("Can't run test with -javaagent"); @@ -877,10 +877,10 @@ public class TestFramework { private void runTestVM(List additionalFlags) { TestVMProcess testVMProcess = new TestVMProcess(additionalFlags, testClass, helperClasses, defaultWarmup, - isAllowNotCompilable, testClassesOnBootClassPath); + allowNotCompilable, testClassesOnBootClassPath); if (shouldVerifyIR) { try { - TestClassParser testClassParser = new TestClassParser(testClass, isAllowNotCompilable); + TestClassParser testClassParser = new TestClassParser(testClass, allowNotCompilable); Matchable testClassMatchable = testClassParser.parse(testVMProcess.getHotspotPidFileName(), testVMProcess.getApplicableIRRules()); IRMatcher matcher = new IRMatcher(testClassMatchable); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java index a172dce1991..931d687b0bc 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java @@ -38,7 +38,6 @@ import java.io.File; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; /** * This class prepares, creates, and runs the Test VM with verification of proper termination. The class also stores @@ -108,7 +107,7 @@ public class TestVMProcess { cmds.add("-XX:+UnlockDiagnosticVMOptions"); cmds.add("-XX:+WhiteBoxAPI"); // Ignore CompileCommand flags which have an impact on the profiling information. - List jtregVMFlags = Arrays.stream(Utils.getTestJavaOpts()).filter(s -> !s.contains("CompileThreshold")).collect(Collectors.toList()); + List jtregVMFlags = Arrays.stream(Utils.getTestJavaOpts()).filter(s -> !s.contains("CompileThreshold")).toList(); if (!PREFER_COMMAND_LINE_FLAGS) { cmds.addAll(jtregVMFlags); } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java index 714af5c6519..893312c5196 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/IRMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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,6 +32,7 @@ import compiler.lib.ir_framework.driver.irmatching.Matchable; import compiler.lib.ir_framework.driver.irmatching.MatchableMatcher; import compiler.lib.ir_framework.driver.irmatching.irrule.IRRule; import compiler.lib.ir_framework.driver.irmatching.parser.VMInfo; +import compiler.lib.ir_framework.driver.network.testvm.java.IRRuleIds; import compiler.lib.ir_framework.shared.TestFormat; import compiler.lib.ir_framework.shared.TestFormatException; @@ -53,14 +54,14 @@ public class IRMethod implements IRMethodMatchable { private final Method method; private final MatchableMatcher matcher; - public IRMethod(Method method, int[] ruleIds, IR[] irAnnos, Compilation compilation, VMInfo vmInfo) { + public IRMethod(Method method, IRRuleIds irRuleIds, IR[] irAnnos, Compilation compilation, VMInfo vmInfo) { this.method = method; - this.matcher = new MatchableMatcher(createIRRules(method, ruleIds, irAnnos, compilation, vmInfo)); + this.matcher = new MatchableMatcher(createIRRules(method, irRuleIds, irAnnos, compilation, vmInfo)); } - private List createIRRules(Method method, int[] ruleIds, IR[] irAnnos, Compilation compilation, VMInfo vmInfo) { + private List createIRRules(Method method, IRRuleIds irRuleIds, IR[] irAnnos, Compilation compilation, VMInfo vmInfo) { List irRules = new ArrayList<>(); - for (int ruleId : ruleIds) { + for (int ruleId : irRuleIds) { try { irRules.add(new IRRule(ruleId, irAnnos[ruleId - 1], compilation, vmInfo)); } catch (TestFormatException e) { @@ -89,13 +90,12 @@ public class IRMethod implements IRMethodMatchable { return new IRMethodMatchResult(method, matcher.match()); } - List match; for (int i = 0; i < 10; i++) { // warm up - match = matcher.match(); + matcher.match(); } long startTime = System.nanoTime(); - match = matcher.match(); + List match = matcher.match(); long endTime = System.nanoTime(); long duration = (endTime - startTime); System.out.println("Verifying IR rules for " + name() + ": " + duration + " ns = " + (duration / 1000000) + " ms"); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/NotCompilableIRMethod.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/NotCompilableIRMethod.java index d74825c8d4f..cb99ea219fe 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/NotCompilableIRMethod.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/NotCompilableIRMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -24,8 +24,7 @@ package compiler.lib.ir_framework.driver.irmatching.irmethod; import compiler.lib.ir_framework.IR; -import compiler.lib.ir_framework.Run; -import compiler.lib.ir_framework.RunMode; +import compiler.lib.ir_framework.Test; import java.lang.reflect.Method; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/NotCompilableIRMethodMatchResult.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/NotCompilableIRMethodMatchResult.java index 011b596cc9e..bea8dc3ab27 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/NotCompilableIRMethodMatchResult.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/irmethod/NotCompilableIRMethodMatchResult.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -23,8 +23,7 @@ package compiler.lib.ir_framework.driver.irmatching.irmethod; -import compiler.lib.ir_framework.Run; -import compiler.lib.ir_framework.RunMode; +import compiler.lib.ir_framework.Test; import compiler.lib.ir_framework.driver.irmatching.MatchResult; import compiler.lib.ir_framework.driver.irmatching.visitor.MatchResultVisitor; diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java index 7aaf0c2b285..be44c3f3d91 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/ApplicableIRRulesParser.java @@ -26,12 +26,15 @@ package compiler.lib.ir_framework.driver.irmatching.parser; import compiler.lib.ir_framework.IR; import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.HotSpotPidFileParser; +import compiler.lib.ir_framework.driver.network.testvm.java.IRRuleIds; import compiler.lib.ir_framework.shared.TestFormat; import compiler.lib.ir_framework.shared.TestFrameworkException; import compiler.lib.ir_framework.test.ApplicableIRRulesPrinter; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -75,15 +78,15 @@ public class ApplicableIRRulesParser { * assembly output in {@link HotSpotPidFileParser}. */ private void createTestMethodMap(String applicableIRRules, Class testClass) { - Map irRulesMap = parseApplicableIRRules(applicableIRRules); + Map irRulesMap = parseApplicableIRRules(applicableIRRules); createTestMethodsWithApplicableIRRules(testClass, irRulesMap); } /** * Read the Applicable IR Rules emitted by the Test VM to decide if an @IR rule must be checked for a method. */ - private Map parseApplicableIRRules(String applicableIRRules) { - Map irRulesMap = new HashMap<>(); + private Map parseApplicableIRRules(String applicableIRRules) { + Map irRulesMap = new HashMap<>(); String[] applicableIRRulesLines = getApplicableIRRulesLines(applicableIRRules); for (String s : applicableIRRulesLines) { String line = s.trim(); @@ -92,8 +95,8 @@ public class ApplicableIRRulesParser { throw new TestFrameworkException("Invalid Applicable IR Rules format. No comma found: " + splitLine[0]); } String testName = splitLine[0]; - int[] irRulesIdx = getRuleIndexes(splitLine); - irRulesMap.put(testName, irRulesIdx); + IRRuleIds irRuleIds = parseIrRulesIds(splitLine); + irRulesMap.put(testName, irRuleIds); } return irRulesMap; } @@ -116,24 +119,24 @@ public class ApplicableIRRulesParser { /** * Parse rule indexes from a single line of the Applicable IR Rules in the format: */ - private int[] getRuleIndexes(String[] splitLine) { - int[] irRulesIdx = new int[splitLine.length - 1]; + private IRRuleIds parseIrRulesIds(String[] splitLine) { + List irRuleIds = new ArrayList<>(); for (int i = 1; i < splitLine.length; i++) { try { - irRulesIdx[i - 1] = Integer.parseInt(splitLine[i]); + irRuleIds.add(Integer.parseInt(splitLine[i])); } catch (NumberFormatException e) { throw new TestFrameworkException("Invalid Applicable IR Rules format. No number found: " + splitLine[i]); } } - return irRulesIdx; + return new IRRuleIds(irRuleIds); } - private void createTestMethodsWithApplicableIRRules(Class testClass, Map irRulesMap) { + private void createTestMethodsWithApplicableIRRules(Class testClass, Map irRulesMap) { for (Method m : testClass.getDeclaredMethods()) { IR[] irAnnos = m.getAnnotationsByType(IR.class); if (irAnnos.length > 0) { // Validation of legal @IR attributes and placement of the annotation was already done in Test VM. - int[] irRuleIds = irRulesMap.get(m.getName()); + IRRuleIds irRuleIds = irRulesMap.get(m.getName()); validateIRRuleIds(m, irAnnos, irRuleIds); if (hasAnyApplicableIRRules(irRuleIds)) { testMethods.put(m.getName(), new TestMethod(m, irAnnos, irRuleIds)); @@ -142,18 +145,18 @@ public class ApplicableIRRulesParser { } } - private void validateIRRuleIds(Method m, IR[] irAnnos, int[] ids) { - TestFramework.check(ids != null, "Should find method name in validIrRulesMap for " + m); - TestFramework.check(ids.length > 0, "Did not find any rule indices for " + m); - TestFramework.check((ids[0] >= 1 || ids[0] == ApplicableIRRulesPrinter.NO_RULE_APPLIED) - && ids[ids.length - 1] <= irAnnos.length, + private void validateIRRuleIds(Method m, IR[] irAnnos, IRRuleIds irRuleIds) { + TestFramework.check(irRuleIds != null, "Should find method name in validIrRulesMap for " + m); + TestFramework.check(!irRuleIds.isEmpty(), "Did not find any rule indices for " + m); + TestFramework.check((irRuleIds.first() >= 1 || irRuleIds.first() == ApplicableIRRulesPrinter.NO_RULE_APPLIED) + && irRuleIds.last() <= irAnnos.length, "Invalid IR rule index found in validIrRulesMap for " + m); } /** * Does the list of IR rules contain any applicable IR rules for the given conditions? */ - private boolean hasAnyApplicableIRRules(int[] irRuleIds) { - return irRuleIds[0] != ApplicableIRRulesPrinter.NO_RULE_APPLIED; + private boolean hasAnyApplicableIRRules(IRRuleIds irRuleIds) { + return irRuleIds.first() != ApplicableIRRulesPrinter.NO_RULE_APPLIED; } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.java index d7a10acd1cd..46a237576e6 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/IRMethodBuilder.java @@ -71,9 +71,9 @@ class IRMethodBuilder { Test[] testAnnos = testMethod.method().getAnnotationsByType(Test.class); boolean allowMethodNotCompilable = allowNotCompilable || testAnnos[0].allowNotCompilable(); if (allowMethodNotCompilable) { - return new NotCompilableIRMethod(testMethod.method(), testMethod.irRuleIds().length); + return new NotCompilableIRMethod(testMethod.method(), testMethod.irRuleIds().count()); } else { - return new NotCompiledIRMethod(testMethod.method(), testMethod.irRuleIds().length); + return new NotCompiledIRMethod(testMethod.method(), testMethod.irRuleIds().count()); } } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethod.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethod.java index 43f27c80ac6..37989d8810b 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethod.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestMethod.java @@ -26,6 +26,7 @@ package compiler.lib.ir_framework.driver.irmatching.parser; import compiler.lib.ir_framework.IR; import compiler.lib.ir_framework.driver.irmatching.irmethod.IRMethod; import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.LoggedMethod; +import compiler.lib.ir_framework.driver.network.testvm.java.IRRuleIds; import java.lang.reflect.Method; @@ -40,9 +41,9 @@ import java.lang.reflect.Method; public class TestMethod { private final Method method; private final IR[] irAnnos; - private final int[] irRuleIds; + private final IRRuleIds irRuleIds; - public TestMethod(Method m, IR[] irAnnos, int[] irRuleIds) { + public TestMethod(Method m, IR[] irAnnos, IRRuleIds irRuleIds) { this.method = m; this.irAnnos = irAnnos; this.irRuleIds = irRuleIds; @@ -56,7 +57,7 @@ public class TestMethod { return irAnnos; } - public int[] irRuleIds() { + public IRRuleIds irRuleIds() { return irRuleIds; } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/IRRuleIds.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/IRRuleIds.java new file mode 100644 index 00000000000..b8ea1765b4f --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/IRRuleIds.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2026, 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. + * + * 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 compiler.lib.ir_framework.driver.network.testvm.java; + +import compiler.lib.ir_framework.IR; +import compiler.lib.ir_framework.driver.irmatching.irmethod.IRMethod; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Stream; + +/** + * Class to hold the indices of the applicable {@link IR @IR} rules of an {@link IRMethod}. + */ +public class IRRuleIds implements Iterable { + private final List ruleIds; + + public IRRuleIds(List ruleIds) { + this.ruleIds = ruleIds; + } + + public int first() { + return ruleIds.getFirst(); + } + + public int last() { + return ruleIds.getLast(); + } + + public boolean isEmpty() { + return ruleIds.isEmpty(); + } + + public int count() { + return ruleIds.size(); + } + + @Override + public Iterator iterator() { + return ruleIds.iterator(); + } + + public Stream stream() { + return ruleIds.stream(); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java index c5ab318d67d..14551141cd7 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java @@ -92,7 +92,7 @@ public class TestVM { static final boolean XCOMP = Platform.isComp(); static final boolean VERBOSE = Boolean.getBoolean("Verbose"); - private static final boolean PRINT_TIMES = Boolean.getBoolean("PrintTimes"); + private static final boolean PRINT_TIMES = Boolean.getBoolean("PrintTimes") || VERBOSE; public static final boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler"); static final boolean EXCLUDE_RANDOM = Boolean.getBoolean("ExcludeRandom"); private static final String TESTLIST = System.getProperty("Test", ""); @@ -588,6 +588,11 @@ public class TestVM { } private void checkTestAnnotations(Method m, Test testAnno) { + List overloads = Arrays.stream(testClass.getDeclaredMethods()).filter(other -> !m.equals(other) && m.getName().equals(other.getName())).toList(); + TestFormat.check(overloads.isEmpty(), + "Cannot overload @Test methods, but method " + m + " has " + overloads.size() + " overload" + (overloads.size() == 1 ? "" : "s") + ":" + + overloads.stream().map(String::valueOf).collect(Collectors.joining("\n - ", "\n - ", "")) + ); TestFormat.check(!testMethodMap.containsKey(m.getName()), "Cannot overload two @Test methods: " + m + ", " + testMethodMap.get(m.getName())); TestFormat.check(testAnno != null, m + " must be a method with a @Test annotation"); @@ -823,14 +828,14 @@ public class TestVM { forceCompileMap.forEach((key, value) -> builder.append("- ").append(key).append(" at CompLevel.").append(value) .append(System.lineSeparator())); throw new TestRunException("Could not force compile the following @ForceCompile methods:" - + System.lineSeparator() + builder.toString()); + + System.lineSeparator() + builder); } /** * Once all framework tests are collected, they are run in this method. */ private void runTests() { - TreeMap durations = (PRINT_TIMES || VERBOSE) ? new TreeMap<>() : null; + TreeMap durations = PRINT_TIMES ? new TreeMap<>() : null; long startTime = System.nanoTime(); List testList; boolean testFilterPresent = testFilterPresent(); @@ -867,11 +872,11 @@ public class TestVM { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); - builder.append(test.toString()).append(":").append(System.lineSeparator()).append(sw.toString()) + builder.append(test).append(":").append(System.lineSeparator()).append(sw) .append(System.lineSeparator()).append(System.lineSeparator()); failures++; } - if (PRINT_TIMES || VERBOSE) { + if (PRINT_TIMES) { long endTime = System.nanoTime(); long duration = (endTime - startTime); durations.put(duration, test.getName()); @@ -886,7 +891,7 @@ public class TestVM { } // Print execution times - if (VERBOSE || PRINT_TIMES) { + if (PRINT_TIMES) { TestFrameworkSocket.write("Test execution times:", PRINT_TIMES_TAG, true); for (Map.Entry entry : durations.entrySet()) { TestFrameworkSocket.write(String.format("%-25s%15d ns%n", entry.getValue() + ":", entry.getKey()), @@ -898,7 +903,7 @@ public class TestVM { // Finally, report all occurred exceptions in a nice format. String msg = System.lineSeparator() + System.lineSeparator() + "Test Failures (" + failures + ")" + System.lineSeparator() + "----------------" + "-".repeat(String.valueOf(failures).length()); - throw new TestRunException(msg + System.lineSeparator() + builder.toString()); + throw new TestRunException(msg + System.lineSeparator() + builder); } } diff --git a/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java b/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java new file mode 100644 index 00000000000..502afc56548 --- /dev/null +++ b/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026 IBM Corporation. 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. + * + * 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. + */ + +/** + * @test + * @bug 8350330 + * @summary C2: PhaseIdealLoop::add_parse_predicate() should mirror GraphKit::add_parse_predicate() + * @library /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-BackgroundCompilation -XX:-ShortRunningLongLoop -XX:-UseOnStackReplacement + * -XX:CompileOnly=*TestLoopNestTooManyTraps::test1 -XX:LoopMaxUnroll=0 + * compiler.longcountedloops.TestLoopNestTooManyTraps + */ + +package compiler.longcountedloops; + +import java.lang.reflect.Method; +import java.util.Objects; +import jdk.test.whitebox.WhiteBox; + +public class TestLoopNestTooManyTraps { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + + public static void main(String[] args) throws NoSuchMethodException { + Method methodTest1 = TestLoopNestTooManyTraps.class.getDeclaredMethod("test1", int.class, long.class); + + for (int j = 0; j < 10; j++) { + System.out.println("iteration " + j); + for (int i = 0; i < 20_000; i++) { + test1(1000, 1000); + } + if (!WHITE_BOX.isMethodCompiled(methodTest1)) { + throw new RuntimeException("test1 should be compiled"); + } + System.out.println("iteration 2 " + j); + try { + test1(10000, 1000); + } catch (IndexOutOfBoundsException ioobe) { + } + if (j <= 1) { + if (WHITE_BOX.isMethodCompiled(methodTest1)) { + throw new RuntimeException("test1 should have deoptimized at iteration " + j); + } + } else { + if (!WHITE_BOX.isMethodCompiled(methodTest1)) { + throw new RuntimeException("test1 shouldn't have deoptimized"); + } + } + } + } + + private static void test1(int stop, long length) { + for (int i = 0; i < stop; i++) { + Objects.checkIndex(i, length); + } + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorBoxExpandPhi.java b/test/hotspot/jtreg/compiler/vectorapi/VectorBoxExpandPhi.java new file mode 100644 index 00000000000..936d24e4767 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorBoxExpandPhi.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2026, 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. + * + * 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 compiler.vectorapi; + +import jdk.incubator.vector.*; + +/* + * @test + * @bug 8374903 + * @summary C2 crashes when VectorBox Phi and vector Phi have different regions + * @modules jdk.incubator.vector + * @library /test/lib + * + * @run main/othervm -Xbatch -XX:CompileCommand=compileonly,compiler.vectorapi.VectorBoxExpandPhi::test compiler.vectorapi.VectorBoxExpandPhi + */ +public class VectorBoxExpandPhi { + public static void main(String[] args) { + for (int i = 0; i < 10_000; i++) { + test(); + } + } + + public static Object test() { + var v0 = DoubleVector.broadcast(DoubleVector.SPECIES_128, 1.0); + var v1 = (FloatVector)v0.convertShape(VectorOperators.Conversion.ofCast(double.class, float.class), FloatVector.SPECIES_64, 0); + var v2 = (FloatVector)v1.convertShape(VectorOperators.Conversion.ofReinterpret(float.class, float.class), FloatVector.SPECIES_64, 0); + return v2; + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorBoxExpandProj.java b/test/hotspot/jtreg/compiler/vectorapi/VectorBoxExpandProj.java new file mode 100644 index 00000000000..5d152834b17 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorBoxExpandProj.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2026, 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. + * + * 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 compiler.vectorapi; + +import jdk.incubator.vector.*; + +/* + * @test + * @bug 8375010 + * @summary C2 crashes when expanding VectorBox with Proj input from vector math call + * @modules jdk.incubator.vector + * @library /test/lib + * + * @run main/othervm -Xbatch -XX:CompileCommand=compileonly,compiler.vectorapi.VectorBoxExpandProj::test compiler.vectorapi.VectorBoxExpandProj + */ +public class VectorBoxExpandProj { + static boolean b; + + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + b = !b; + test(); + } + System.out.println("PASS"); + } + + // TAN returns Proj (not VectorNode). Phi merging creates VectorBox(Phi, Proj). + // expand_vbox_node_helper must handle Proj inputs with vector type. + static Object test() { + var t = DoubleVector.broadcast(DoubleVector.SPECIES_128, 1.0).lanewise(VectorOperators.TAN); + return b ? t.convertShape(VectorOperators.Conversion.ofCast(double.class, double.class), DoubleVector.SPECIES_128, 0) : t; + } +} diff --git a/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java b/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java new file mode 100644 index 00000000000..e02563c2fc2 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java @@ -0,0 +1,556 @@ +/* + * Copyright (c) 2026, 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. + * + * 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. + */ + +/* + * @test id=vanilla + * @bug 8373026 + * @summary Test auto vectorization and Vector API with some vector + * algorithms. Related benchmark: VectorAlgorithms.java + * @library /test/lib / + * @modules jdk.incubator.vector + * @run driver ${test.main.class} + */ + +/* + * @test id=noSuperWord + * @bug 8373026 + * @library /test/lib / + * @modules jdk.incubator.vector + * @run driver ${test.main.class} -XX:-UseSuperWord + */ + +/* + * @test id=noOptimizeFill + * @bug 8373026 + * @library /test/lib / + * @modules jdk.incubator.vector + * @run driver ${test.main.class} -XX:-OptimizeFill + */ + +package compiler.vectorization; + +import java.util.Map; +import java.util.HashMap; +import jdk.test.lib.Utils; +import java.util.Random; +import java.lang.foreign.*; + +import compiler.lib.ir_framework.*; +import compiler.lib.generators.*; +import static compiler.lib.generators.Generators.G; +import compiler.lib.verify.*; + +/** + * The goal of this benchmark is to show the power of auto vectorization + * and the Vector API. + * + * Please only modify this benchark in synchronization with the JMH benchmark: + * micro/org/openjdk/bench/vm/compiler/VectorAlgorithms.java + */ +public class TestVectorAlgorithms { + private static final Random RANDOM = Utils.getRandomInstance(); + private static final RestrictableGenerator INT_GEN = Generators.G.ints(); + + interface TestFunction { + Object run(int i); + } + + Map> testGroups = new HashMap>(); + + VectorAlgorithmsImpl.Data d; + + public static void main(String[] args) { + TestFramework framework = new TestFramework(); + framework.addFlags("--add-modules=jdk.incubator.vector", + "-XX:CompileCommand=inline,*VectorAlgorithmsImpl*::*"); + framework.addFlags(args); + framework.start(); + } + + public TestVectorAlgorithms () { + testGroups.put("fillI", new HashMap()); + testGroups.get("fillI").put("fillI_loop", i -> { return fillI_loop(d.rI1); }); + testGroups.get("fillI").put("fillI_VectorAPI", i -> { return fillI_VectorAPI(d.rI2); }); + testGroups.get("fillI").put("fillI_Arrays", i -> { return fillI_Arrays(d.rI3); }); + + testGroups.put("iotaI", new HashMap()); + testGroups.get("iotaI").put("iotaI_loop", i -> { return iotaI_loop(d.rI1); }); + testGroups.get("iotaI").put("iotaI_VectorAPI", i -> { return iotaI_VectorAPI(d.rI2); }); + + testGroups.put("copyI", new HashMap()); + testGroups.get("copyI").put("copyI_loop", i -> { return copyI_loop(d.aI, d.rI1); }); + testGroups.get("copyI").put("copyI_VectorAPI", i -> { return copyI_VectorAPI(d.aI, d.rI2); }); + testGroups.get("copyI").put("copyI_System_arraycopy", i -> { return copyI_System_arraycopy(d.aI, d.rI3); }); + + testGroups.put("mapI", new HashMap()); + testGroups.get("mapI").put("mapI_loop", i -> { return mapI_loop(d.aI, d.rI1); }); + testGroups.get("mapI").put("mapI_VectorAPI", i -> { return mapI_VectorAPI(d.aI, d.rI2); }); + + testGroups.put("reduceAddI", new HashMap()); + testGroups.get("reduceAddI").put("reduceAddI_loop", i -> { return reduceAddI_loop(d.aI); }); + testGroups.get("reduceAddI").put("reduceAddI_reassociate", i -> { return reduceAddI_reassociate(d.aI); }); + testGroups.get("reduceAddI").put("reduceAddI_VectorAPI_naive", i -> { return reduceAddI_VectorAPI_naive(d.aI); }); + testGroups.get("reduceAddI").put("reduceAddI_VectorAPI_reduction_after_loop", i -> { return reduceAddI_VectorAPI_reduction_after_loop(d.aI); }); + + testGroups.put("dotProductF", new HashMap()); + testGroups.get("dotProductF").put("dotProductF_loop", i -> { return dotProductF_loop(d.aF, d.bF); }); + testGroups.get("dotProductF").put("dotProductF_VectorAPI_naive", i -> { return dotProductF_VectorAPI_naive(d.aF, d.bF); }); + testGroups.get("dotProductF").put("dotProductF_VectorAPI_reduction_after_loop", i -> { return dotProductF_VectorAPI_reduction_after_loop(d.aF, d.bF); }); + + testGroups.put("hashCodeB", new HashMap()); + testGroups.get("hashCodeB").put("hashCodeB_loop", i -> { return hashCodeB_loop(d.aB); }); + testGroups.get("hashCodeB").put("hashCodeB_Arrays", i -> { return hashCodeB_Arrays(d.aB); }); + testGroups.get("hashCodeB").put("hashCodeB_VectorAPI_v1", i -> { return hashCodeB_VectorAPI_v1(d.aB); }); + testGroups.get("hashCodeB").put("hashCodeB_VectorAPI_v2", i -> { return hashCodeB_VectorAPI_v2(d.aB); }); + + testGroups.put("scanAddI", new HashMap()); + testGroups.get("scanAddI").put("scanAddI_loop", i -> { return scanAddI_loop(d.aI, d.rI1); }); + testGroups.get("scanAddI").put("scanAddI_loop_reassociate", i -> { return scanAddI_loop_reassociate(d.aI, d.rI2); }); + testGroups.get("scanAddI").put("scanAddI_VectorAPI_permute_add", i -> { return scanAddI_VectorAPI_permute_add(d.aI, d.rI4); }); + + testGroups.put("findMinIndexI", new HashMap()); + testGroups.get("findMinIndexI").put("findMinIndexI_loop", i -> { return findMinIndexI_loop(d.aI); }); + testGroups.get("findMinIndexI").put("findMinIndexI_VectorAPI", i -> { return findMinIndexI_VectorAPI(d.aI); }); + + testGroups.put("findI", new HashMap()); + testGroups.get("findI").put("findI_loop", i -> { return findI_loop(d.aI, d.eI[i]); }); + testGroups.get("findI").put("findI_VectorAPI", i -> { return findI_VectorAPI(d.aI, d.eI[i]); }); + + testGroups.put("reverseI", new HashMap()); + testGroups.get("reverseI").put("reverseI_loop", i -> { return reverseI_loop(d.aI, d.rI1); }); + testGroups.get("reverseI").put("reverseI_VectorAPI", i -> { return reverseI_VectorAPI(d.aI, d.rI2); }); + + testGroups.put("filterI", new HashMap()); + testGroups.get("filterI").put("filterI_loop", i -> { return filterI_loop(d.aI, d.rI1, d.eI[i]); }); + testGroups.get("filterI").put("filterI_VectorAPI", i -> { return filterI_VectorAPI(d.aI, d.rI2, d.eI[i]); }); + + testGroups.put("reduceAddIFieldsX4", new HashMap()); + testGroups.get("reduceAddIFieldsX4").put("reduceAddIFieldsX4_loop", i -> { return reduceAddIFieldsX4_loop(d.oopsX4, d.memX4); }); + testGroups.get("reduceAddIFieldsX4").put("reduceAddIFieldsX4_VectorAPI", i -> { return reduceAddIFieldsX4_VectorAPI(d.oopsX4, d.memX4); }); + + testGroups.put("lowerCaseB", new HashMap()); + testGroups.get("lowerCaseB").put("lowerCaseB_loop", i -> { return lowerCaseB_loop(d.strB, d.rB1); }); + testGroups.get("lowerCaseB").put("lowerCaseB_VectorAPI_v1", i -> { return lowerCaseB_VectorAPI_v1(d.strB, d.rB2); }); + testGroups.get("lowerCaseB").put("lowerCaseB_VectorAPI_v2", i -> { return lowerCaseB_VectorAPI_v2(d.strB, d.rB3); }); + } + + @Warmup(100) + @Run(test = {"fillI_loop", + "fillI_VectorAPI", + "fillI_Arrays", + "iotaI_loop", + "iotaI_VectorAPI", + "copyI_loop", + "copyI_VectorAPI", + "copyI_System_arraycopy", + "mapI_loop", + "mapI_VectorAPI", + "reduceAddI_loop", + "reduceAddI_reassociate", + "reduceAddI_VectorAPI_naive", + "reduceAddI_VectorAPI_reduction_after_loop", + "dotProductF_loop", + "dotProductF_VectorAPI_naive", + "dotProductF_VectorAPI_reduction_after_loop", + "hashCodeB_loop", + "hashCodeB_Arrays", + "hashCodeB_VectorAPI_v1", + "hashCodeB_VectorAPI_v2", + "scanAddI_loop", + "scanAddI_loop_reassociate", + "scanAddI_VectorAPI_permute_add", + "findMinIndexI_loop", + "findMinIndexI_VectorAPI", + "findI_loop", + "findI_VectorAPI", + "reverseI_loop", + "reverseI_VectorAPI", + "filterI_loop", + "filterI_VectorAPI", + "reduceAddIFieldsX4_loop", + "reduceAddIFieldsX4_VectorAPI", + "lowerCaseB_loop", + "lowerCaseB_VectorAPI_v1", + "lowerCaseB_VectorAPI_v2"}) + public void runTests(RunInfo info) { + // Repeat many times, so that we also have multiple iterations for post-warmup to potentially recompile + int iters = info.isWarmUp() ? 1 : 20; + for (int iter = 0; iter < iters; iter++) { + // Set up random inputs, random size is important to stress tails. + int size = 100_000 + RANDOM.nextInt(10_000); + int seed = RANDOM.nextInt(); + int numXObjects = 10_000; + d = new VectorAlgorithmsImpl.Data(size, seed, numXObjects); + + // Run all tests + for (Map.Entry> group_entry : testGroups.entrySet()) { + String group_name = group_entry.getKey(); + Map group = group_entry.getValue(); + Object gold = null; + String gold_name = "NONE"; + for (Map.Entry entry : group.entrySet()) { + String name = entry.getKey(); + TestFunction test = entry.getValue(); + Object result = test.run(iter); + if (gold == null) { + gold = result; + gold_name = name; + } else { + try { + Verify.checkEQ(gold, result); + } catch (VerifyException e) { + throw new RuntimeException("Verify.checkEQ failed for group " + group_name + + ", gold " + gold_name + ", test " + name, e); + } + } + } + } + } + } + + @Test + @IR(counts = {IRNode.REPLICATE_I, "= 1", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfAnd = {"UseSuperWord", "true", "OptimizeFill", "false"}) + @IR(counts = {".*CallLeafNoFP.*jint_fill.*", "= 1"}, + phase = CompilePhase.BEFORE_MATCHING, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIf = {"OptimizeFill", "true"}) + // By default, the fill intrinsic "jint_fill" is used, but we can disable + // the detection of the fill loop, and then we auto vectorize. + public Object fillI_loop(int[] r) { + return VectorAlgorithmsImpl.fillI_loop(r); + } + + @Test + @IR(counts = {IRNode.REPLICATE_I, "= 1", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public Object fillI_VectorAPI(int[] r) { + return VectorAlgorithmsImpl.fillI_VectorAPI(r); + } + + @Test + // Arrays.fill is not necessarily inlined, so we can't check + // for vectors in the IR. + public Object fillI_Arrays(int[] r) { + return VectorAlgorithmsImpl.fillI_Arrays(r); + } + + @Test + @IR(counts = {IRNode.POPULATE_INDEX, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"avx2", "true", "sve", "true"}, + applyIf = {"UseSuperWord", "true"}) + // Note: the Vector API example below can also vectorize for AVX, + // because it does not use a PopulateIndex. + public Object iotaI_loop(int[] r) { + return VectorAlgorithmsImpl.iotaI_loop(r); + } + + @Test + @IR(counts = {IRNode.ADD_VI, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeature = {"sse4.1", "true"}) + // Note: also works with NEON/asimd, but only with TieredCompilation. + public Object iotaI_VectorAPI(int[] r) { + return VectorAlgorithmsImpl.iotaI_VectorAPI(r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIf = {"UseSuperWord", "true"}) + public Object copyI_loop(int[] a, int[] r) { + return VectorAlgorithmsImpl.copyI_loop(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public Object copyI_VectorAPI(int[] a, int[] r) { + return VectorAlgorithmsImpl.copyI_VectorAPI(a, r); + } + + @Test + @IR(counts = {".*CallLeafNoFP.*jint_disjoint_arraycopy.*", "= 1"}, + phase = CompilePhase.BEFORE_MATCHING, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public Object copyI_System_arraycopy(int[] a, int[] r) { + return VectorAlgorithmsImpl.copyI_System_arraycopy(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.MUL_VI, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIf = {"UseSuperWord", "true"}) + public Object mapI_loop(int[] a, int[] r) { + return VectorAlgorithmsImpl.mapI_loop(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.MUL_VI, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public Object mapI_VectorAPI(int[] a, int[] r) { + return VectorAlgorithmsImpl.mapI_VectorAPI(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.ADD_REDUCTION_VI, "> 0", + IRNode.ADD_VI, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIf = {"UseSuperWord", "true"}) + public int reduceAddI_loop(int[] a) { + return VectorAlgorithmsImpl.reduceAddI_loop(a); + } + + @Test + public int reduceAddI_reassociate(int[] a) { + return VectorAlgorithmsImpl.reduceAddI_reassociate(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.ADD_REDUCTION_VI, "> 0"}, // reduceLanes inside loop + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public int reduceAddI_VectorAPI_naive(int[] a) { + return VectorAlgorithmsImpl.reduceAddI_VectorAPI_naive(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", + IRNode.ADD_REDUCTION_V, "> 0", + IRNode.MUL_VF, "> 0"}, + applyIfCPUFeature = {"sse4.1", "true"}, + applyIf = {"UseSuperWord", "true"}) + // See also TestReduction.floatAddDotProduct + public float dotProductF_loop(float[] a, float[] b) { + return VectorAlgorithmsImpl.dotProductF_loop(a, b); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", + IRNode.ADD_REDUCTION_V, "> 0", + IRNode.MUL_VF, "> 0"}, + applyIfCPUFeature = {"sse4.1", "true"}, + applyIf = {"UseSuperWord", "true"}) + public float dotProductF_VectorAPI_naive(float[] a, float[] b) { + return VectorAlgorithmsImpl.dotProductF_VectorAPI_naive(a, b); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", + IRNode.ADD_REDUCTION_V, "> 0", + IRNode.MUL_VF, "> 0"}, + applyIfCPUFeature = {"sse4.1", "true"}, + applyIf = {"UseSuperWord", "true"}) + public float dotProductF_VectorAPI_reduction_after_loop(float[] a, float[] b) { + return VectorAlgorithmsImpl.dotProductF_VectorAPI_reduction_after_loop(a, b); + } + + @Test + public int hashCodeB_loop(byte[] a) { + return VectorAlgorithmsImpl.hashCodeB_loop(a); + } + + @Test + public int hashCodeB_Arrays(byte[] a) { + return VectorAlgorithmsImpl.hashCodeB_Arrays(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE_8, "> 0", + IRNode.VECTOR_CAST_B2I, IRNode.VECTOR_SIZE_8, "> 0", + IRNode.MUL_VI, IRNode.VECTOR_SIZE_8, "> 0", + IRNode.ADD_VI, IRNode.VECTOR_SIZE_8, "> 0", + IRNode.ADD_REDUCTION_VI, "> 0"}, + applyIfCPUFeature = {"avx2", "true"}) + public int hashCodeB_VectorAPI_v1(byte[] a) { + return VectorAlgorithmsImpl.hashCodeB_VectorAPI_v1(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", + IRNode.MUL_VI, "> 0", + IRNode.ADD_VI, "> 0", + IRNode.ADD_REDUCTION_VI, "> 0"}, + applyIfCPUFeature = {"avx2", "true"}) + public int hashCodeB_VectorAPI_v2(byte[] a) { + return VectorAlgorithmsImpl.hashCodeB_VectorAPI_v2(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.ADD_REDUCTION_VI, "> 0", + IRNode.ADD_VI, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public int reduceAddI_VectorAPI_reduction_after_loop(int[] a) { + return VectorAlgorithmsImpl.reduceAddI_VectorAPI_reduction_after_loop(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0", + IRNode.STORE_VECTOR, "= 0"}) + // Currently does not vectorize, but might in the future. + public Object scanAddI_loop(int[] a, int[] r) { + return VectorAlgorithmsImpl.scanAddI_loop(a, r); + } + + @Test + public Object scanAddI_loop_reassociate(int[] a, int[] r) { + return VectorAlgorithmsImpl.scanAddI_loop_reassociate(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.REARRANGE_VI, "> 0", + IRNode.AND_VI, "> 0", + IRNode.ADD_VI, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIf = {"MaxVectorSize", ">=64"}) + public Object scanAddI_VectorAPI_permute_add(int[] a, int[] r) { + return VectorAlgorithmsImpl.scanAddI_VectorAPI_permute_add(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0"}) + // Currently does not vectorize, but might in the future. + public int findMinIndexI_loop(int[] a) { + return VectorAlgorithmsImpl.findMinIndexI_loop(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_BLEND_I, "> 0", + IRNode.MIN_REDUCTION_V, "> 0", + IRNode.ADD_VI, "> 0"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + public int findMinIndexI_VectorAPI(int[] a) { + return VectorAlgorithmsImpl.findMinIndexI_VectorAPI(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0"}) + // Currently does not vectorize, but might in the future. + public int findI_loop(int[] a, int e) { + return VectorAlgorithmsImpl.findI_loop(a, e); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_TEST, "> 0"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + public int findI_VectorAPI(int[] a, int e) { + return VectorAlgorithmsImpl.findI_VectorAPI(a, e); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0", + IRNode.STORE_VECTOR, "= 0"}) + // Currently does not vectorize, but might in the future. + public Object reverseI_loop(int[] a, int[] r) { + return VectorAlgorithmsImpl.reverseI_loop(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.REARRANGE_VI, "> 0", + IRNode.AND_VI, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public Object reverseI_VectorAPI(int[] a, int[] r) { + return VectorAlgorithmsImpl.reverseI_VectorAPI(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0", + IRNode.STORE_VECTOR, "= 0"}) + public Object filterI_loop(int[] a, int[] r, int threshold) { + return VectorAlgorithmsImpl.filterI_loop(a, r, threshold); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_TEST, "> 0", + IRNode.COMPRESS_VI, "> 0", + IRNode.STORE_VECTOR_MASKED, "> 0"}, + applyIfCPUFeature = {"avx2", "true"}) + public Object filterI_VectorAPI(int[] a, int[] r, int threshold) { + return VectorAlgorithmsImpl.filterI_VectorAPI(a, r, threshold); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0"}) + // Currently does not vectorize, but might in the future. + public int reduceAddIFieldsX4_loop(int[] oops, int[] mem) { + return VectorAlgorithmsImpl.reduceAddIFieldsX4_loop(oops, mem); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_TEST, "> 0", + IRNode.LOAD_VECTOR_GATHER_MASKED, "> 0", + IRNode.OR_V_MASK, "> 0", + IRNode.ADD_VI, "> 0", + IRNode.ADD_REDUCTION_VI, "> 0"}, + applyIfCPUFeatureOr = {"avx512", "true", "sve", "true"}) + public int reduceAddIFieldsX4_VectorAPI(int[] oops, int[] mem) { + return VectorAlgorithmsImpl.reduceAddIFieldsX4_VectorAPI(oops, mem); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_B, "= 0"}) + // Currently does not vectorize, but might in the future. + public Object lowerCaseB_loop(byte[] a, byte[] r) { + return VectorAlgorithmsImpl.lowerCaseB_loop(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", + IRNode.ADD_VB, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public Object lowerCaseB_VectorAPI_v1(byte[] a, byte[] r) { + return VectorAlgorithmsImpl.lowerCaseB_VectorAPI_v1(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", + IRNode.ADD_VB, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public Object lowerCaseB_VectorAPI_v2(byte[] a, byte[] r) { + return VectorAlgorithmsImpl.lowerCaseB_VectorAPI_v2(a, r); + } +} diff --git a/test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java b/test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java new file mode 100644 index 00000000000..2dfac704069 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java @@ -0,0 +1,774 @@ +/* + * Copyright (c) 2026, 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. + * + * 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 compiler.vectorization; + +import java.util.Arrays; +import java.util.Random; +import jdk.incubator.vector.*; + +/** + * The code below is supposed to be an exact copy of: + * micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java + */ +public class VectorAlgorithmsImpl { + private static final VectorSpecies SPECIES_I = IntVector.SPECIES_PREFERRED; + private static final VectorSpecies SPECIES_I512 = IntVector.SPECIES_512; + private static final VectorSpecies SPECIES_I256 = IntVector.SPECIES_256; + private static final VectorSpecies SPECIES_B = ByteVector.SPECIES_PREFERRED; + private static final VectorSpecies SPECIES_B64 = ByteVector.SPECIES_64; + private static final VectorSpecies SPECIES_F = FloatVector.SPECIES_PREFERRED; + + // This class stores the input and output arrays. + // The constructor sets up all the data. + // + // IMPORTANT: + // If you want to use some array but do NOT modify it: just use it. + // If you want to use it and DO want to modify it: clone it. This + // ensures that each test gets a separate copy, and that when we + // capture the modified arrays they are different for every method + // and run. + // An alternative to cloning is to use different return arrays for + // different implementations of the same group, e.g. rI1, rI2, ... + // + public static class Data { + public int[] aI; + public int[] rI1; + public int[] rI2; + public int[] rI3; + public int[] rI4; + public int[] eI; + // The test has to use the same index into eI for all implementations. But in the + // benchmark, we'd like to use random indices, so we use the index to advance through + // the array. + public int eI_idx = 0; + + public float[] aF; + public float[] bF; + + public byte[] aB; + public byte[] strB; + public byte[] rB1; + public byte[] rB2; + public byte[] rB3; + + public int[] oopsX4; + public int[] memX4; + + public Data(int size, int seed, int numX4Objects) { + Random random = new Random(seed); + + // int: one input array and multiple output arrays so different implementations can + // store their results to different arrays. + aI = new int[size]; + rI1 = new int[size]; + rI2 = new int[size]; + rI3 = new int[size]; + rI4 = new int[size]; + Arrays.setAll(aI, i -> random.nextInt()); + + // Populate with some random values from aI, and some totally random values. + eI = new int[0x10000]; + for (int i = 0; i < eI.length; i++) { + eI[i] = (random.nextInt(10) == 0) ? random.nextInt() : aI[random.nextInt(size)]; + } + + // X4 oop setup. + // oopsX4 holds "addresses" (i.e. indices), that point to the 16-byte objects in memX4. + oopsX4 = new int[size]; + memX4 = new int[numX4Objects * 4]; + for (int i = 0; i < size; i++) { + // assign either a zero=null, or assign a random oop. + oopsX4[i] = (random.nextInt(10) == 0) ? 0 : random.nextInt(numX4Objects) * 4; + } + // Just fill the whole array with random values. + // The relevant field is only at every "4 * i + 3" though. + memX4 = new int[4 * numX4Objects]; + for (int i = 0; i < memX4.length; i++) { + memX4[i] = random.nextInt(); + } + + // float inputs. To avoid rounding issues, only use small integers. + aF = new float[size]; + bF = new float[size]; + for (int i = 0; i < size; i++) { + aF[i] = random.nextInt(32) - 16; + bF[i] = random.nextInt(32) - 16; + } + + // byte: just random data. + aB = new byte[size]; + strB = new byte[size]; + rB1 = new byte[size]; + rB2 = new byte[size]; + rB3 = new byte[size]; + random.nextBytes(aB); + random.nextBytes(strB); // TODO: special data! + } + } + + public static Object fillI_loop(int[] r) { + for (int i = 0; i < r.length; i++) { + r[i] = 42; + } + return r; + } + + public static Object fillI_Arrays(int[] r) { + Arrays.fill(r, 42); + return r; + } + + public static Object fillI_VectorAPI(int[] r) { + var v = IntVector.broadcast(SPECIES_I, 42); + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + v.intoArray(r, i); + } + for (; i < r.length; i++) { + r[i] = 42; + } + return r; + } + + public static Object iotaI_loop(int[] r) { + for (int i = 0; i < r.length; i++) { + r[i] = i; + } + return r; + } + + public static Object iotaI_VectorAPI(int[] r) { + var iota = IntVector.broadcast(SPECIES_I, 0).addIndex(1); + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + iota.intoArray(r, i); + iota = iota.add(SPECIES_I.length()); + } + for (; i < r.length; i++) { + r[i] = i; + } + return r; + } + + public static Object copyI_loop(int[] a, int[] r) { + for (int i = 0; i < a.length; i++) { + r[i] = a[i]; + } + return r; + } + + public static Object copyI_System_arraycopy(int[] a, int[] r) { + System.arraycopy(a, 0, r, 0, a.length); + return r; + } + + public static Object copyI_VectorAPI(int[] a, int[] r) { + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + v.intoArray(r, i); + } + for (; i < r.length; i++) { + r[i] = a[i]; + } + return r; + } + + public static Object mapI_loop(int[] a, int[] r) { + for (int i = 0; i < a.length; i++) { + r[i] = a[i] * 42; + } + return r; + } + + public static Object mapI_VectorAPI(int[] a, int[] r) { + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + v = v.mul(42); + v.intoArray(r, i); + } + for (; i < r.length; i++) { + r[i] = a[i] * 42; + } + return r; + } + + public static int reduceAddI_loop(int[] a) { + int sum = 0; + for (int i = 0; i < a.length; i++) { + // Relying on simple reduction loop should vectorize since JDK26. + sum += a[i]; + } + return sum; + } + + public static int reduceAddI_reassociate(int[] a) { + int sum = 0; + int i; + for (i = 0; i < a.length - 3; i += 4) { + // Unroll 4x, reassociate inside. + sum += a[i] + a[i + 1] + a[i + 2] + a[i + 3]; + } + for (; i < a.length; i++) { + // Tail + sum += a[i]; + } + return sum; + } + + public static int reduceAddI_VectorAPI_naive(int[] a) { + var sum = 0; + int i; + for (i = 0; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + // reduceLanes in loop is better than scalar performance, but still + // relatively slow. + sum += v.reduceLanes(VectorOperators.ADD); + } + for (; i < a.length; i++) { + sum += a[i]; + } + return sum; + } + + public static int reduceAddI_VectorAPI_reduction_after_loop(int[] a) { + var acc = IntVector.broadcast(SPECIES_I, 0); + int i; + for (i = 0; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + // Element-wide addition into a vector of partial sums is much faster. + // Now, we only need to do a reduceLanes after the loop. + // This works because int-addition is associative and commutative. + acc = acc.add(v); + } + int sum = acc.reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + sum += a[i]; + } + return sum; + } + + public static float dotProductF_loop(float[] a, float[] b) { + float sum = 0; + for (int i = 0; i < a.length; i++) { + sum += a[i] * b[i]; + } + return sum; + } + + public static float dotProductF_VectorAPI_naive(float[] a, float[] b) { + float sum = 0; + int i; + for (i = 0; i < SPECIES_F.loopBound(a.length); i += SPECIES_F.length()) { + var va = FloatVector.fromArray(SPECIES_F, a, i); + var vb = FloatVector.fromArray(SPECIES_F, b, i); + sum += va.mul(vb).reduceLanes(VectorOperators.ADD); + } + for (; i < a.length; i++) { + sum += a[i] * b[i]; + } + return sum; + } + + public static float dotProductF_VectorAPI_reduction_after_loop(float[] a, float[] b) { + var sums = FloatVector.broadcast(SPECIES_F, 0.0f); + int i; + for (i = 0; i < SPECIES_F.loopBound(a.length); i += SPECIES_F.length()) { + var va = FloatVector.fromArray(SPECIES_F, a, i); + var vb = FloatVector.fromArray(SPECIES_F, b, i); + sums = sums.add(va.mul(vb)); + } + float sum = sums.reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + sum += a[i] * b[i]; + } + return sum; + } + + public static int hashCodeB_loop(byte[] a) { + int h = 1; + for (int i = 0; i < a.length; i++) { + h = 31 * h + a[i]; + } + return h; + } + + public static int hashCodeB_Arrays(byte[] a) { + return Arrays.hashCode(a); + } + + // Simplified intrinsic code from C2_MacroAssembler::arrays_hashcode in c2_MacroAssembler_x86.cpp + // + // Ideas that may help understand the code: + // h(i) = 31 * h(i-1) + a[i] + // "unroll" by factor of L=8: + // h(i+8) = h(i) * 31^8 + a[i+1] * 31^7 + a[i+2] * 31^6 + ... + a[i+8] * 1 + // ----------- ------------------------------------------------ + // scalar vector: notice the powers of 31 in reverse + // + // We notice that we can load a[i+1 .. i+8], then element-wise multiply with + // the vector of reversed powers-of-31, and then do reduceLanes(ADD). + // But we can do even better: By looking at multiple such 8-unrolled iterations. + // Instead of applying the "next" factor of "31^8" to the reduced scalar, we can + // already apply it element-wise. That allows us to move the reduction out + // of the loop. + // + // Note: the intrinsic additionally unrolls the loop by a factor of 4, + // but we want to keep thins simple for demonstration purposes. + // + private static int[] REVERSE_POWERS_OF_31 = new int[9]; + static { + int p = 1; + for (int i = REVERSE_POWERS_OF_31.length - 1; i >= 0; i--) { + REVERSE_POWERS_OF_31[i] = p; + p *= 31; + } + } + public static int hashCodeB_VectorAPI_v1(byte[] a) { + int result = 1; // initialValue + var vresult = IntVector.zero(SPECIES_I256); + int next = REVERSE_POWERS_OF_31[0]; // 31^L + var vcoef = IntVector.fromArray(SPECIES_I256, REVERSE_POWERS_OF_31, 1); // powers of 2 in reverse + int i; + for (i = 0; i < SPECIES_B64.loopBound(a.length); i += SPECIES_B64.length()) { + // scalar part: result *= 31^L + result *= next; + // vector part: element-wise apply the next factor and add in the new values. + var vb = ByteVector.fromArray(SPECIES_B64, a, i); + var vi = vb.castShape(SPECIES_I256, 0); + vresult = vresult.mul(next).add(vi); + } + // reduce the partial hashes in the elements, using the reverse list of powers of 2. + result += vresult.mul(vcoef).reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + result = 31 * result + a[i]; + } + return result; + } + + // This second approach follows the idea from this blog post by Otmar Ertl: + // https://www.dynatrace.com/news/blog/java-arrays-hashcode-byte-efficiency-techniques/ + // + // I simplified the algorithm a little, so that it is a bit closer + // to the solution "v1" above. + // + // The major issue with "v1" is that we cannot load a full vector of bytes, + // because of the cast to ints. So we can only fill 1/4 of the maximal + // vector size. The trick here is to do an unrolling of factor 4, from: + // h(i) = 31 * h(i-1) + a[i] + // to: + // h(i+4) = h(i) * 31^4 + a[i + 1] * 31^3 + // + a[i + 2] * 31^2 + // + a[i + 3] * 31^1 + // + a[i + 4] * 31^0 + // The goal is now to compute this value for 4 bytes within a 4 byte + // lane of the vector. One concern is that we start with byte values, + // but need to do int-multiplication with powers of 31. If we instead + // did a byte-multiplication, we could get overflows that we would not + // have had in the int-multiplication. + // One trick that helps with chaning the size of the lanes from byte + // to short to int is doing all operations with unsigned integers. That + // way, we can zero-extend instead of sign-bit extend. The first step + // is thus to convert the bytes into unsigned values. Since byte is in + // range [-128..128), doing "a[i+j] + 128" makes it a positive value, + // allowing for unsigned multiplication. + // h(i+4) = h(i) * 31^4 + a[i + 1] * 31^3 + // + a[i + 2] * 31^2 + // + a[i + 3] * 31^1 + // + a[i + 4] * 31^0 + // = h(i) * 31^4 + (a[i + 1] + 128 - 128) * 31^3 + // + (a[i + 2] + 128 - 128) * 31^2 + // + (a[i + 3] + 128 - 128) * 31^1 + // + (a[i + 4] + 128 - 128) * 31^0 + // = h(i) * 31^4 + (a[i + 1] + 128 ) * 31^3 + // + (a[i + 2] + 128 ) * 31^2 + // + (a[i + 3] + 128 ) * 31^1 + // + (a[i + 4] + 128 ) * 31^0 + // + -128 * (31^3 + 31^2 + 31^1 + 1) + // = h(i) * 31^4 + ((a[i + 1] + 128) * 31 + // + (a[i + 2] + 128 ) * 31^2 + // + ((a[i + 3] + 128) * 31 + // + (a[i + 4] + 128 ) + // + -128 * (31^3 + 31^2 + 31^1 + 1) + // + // Getting from the signed a[i] value to unsigned with +128, we can + // just xor with 0x80=128. Any numbers there in range [-128..0) are + // now in range [0..128). And any numbers that were in range [0..128) + // are now in unsigned range [128..255). What a neat trick! + // + // We then apply a byte->short transition where we crunch 2 bytes + // into one short, applying a multiplication with 31 to one of the + // two bytes. This multiplication cannot overflow in a short. + // then we apply a short->int transition where we crunch 2 shorts + // into one int, applying a multiplication with 31^2 to one of the + // two shorts. This multiplication cannot overflow in an int. + // + public static int hashCodeB_VectorAPI_v2(byte[] a) { + return HashCodeB_VectorAPI_V2.compute(a); + } + + private static class HashCodeB_VectorAPI_V2 { + private static final int L = Math.min(ByteVector.SPECIES_PREFERRED.length(), + IntVector.SPECIES_PREFERRED.length() * 4); + private static final VectorShape SHAPE = VectorShape.forBitSize(8 * L); + private static final VectorSpecies SPECIES_B = SHAPE.withLanes(byte.class); + private static final VectorSpecies SPECIES_I = SHAPE.withLanes(int.class); + + private static int[] REVERSE_POWERS_OF_31_STEP_4 = new int[L / 4 + 1]; + static { + int p = 1; + int step = 31 * 31 * 31 * 31; // step by 4 + for (int i = REVERSE_POWERS_OF_31_STEP_4.length - 1; i >= 0; i--) { + REVERSE_POWERS_OF_31_STEP_4[i] = p; + p *= step; + } + } + + public static int compute(byte[] a) { + int result = 1; // initialValue + int next = REVERSE_POWERS_OF_31_STEP_4[0]; // 31^L + var vcoef = IntVector.fromArray(SPECIES_I, REVERSE_POWERS_OF_31_STEP_4, 1); // W + var vresult = IntVector.zero(SPECIES_I); + int i; + for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { + var vb = ByteVector.fromArray(SPECIES_B, a, i); + // Add 128 to each byte. + var vs = vb.lanewise(VectorOperators.XOR, (byte)0x80) + .reinterpretAsShorts(); + // Each short lane contains 2 bytes, crunch them. + var vi = vs.and((short)0xff) // lower byte + .mul((short)31) + .add(vs.lanewise(VectorOperators.LSHR, 8)) // upper byte + .reinterpretAsInts(); + // Each int contains 2 shorts, crunch them. + var v = vi.and(0xffff) // lower short + .mul(31 * 31) + .add(vi.lanewise(VectorOperators.LSHR, 16)); // upper short + // Add the correction for the 128 additions above. + v = v.add(-128 * (31*31*31 + 31*31 + 31 + 1)); + // Every element of v now contains a crunched int-package of 4 bytes. + result *= next; + vresult = vresult.mul(next).add(v); + } + result += vresult.mul(vcoef).reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + result = 31 * result + a[i]; + } + return result; + } + } + + public static Object scanAddI_loop(int[] a, int[] r) { + int sum = 0; + for (int i = 0; i < a.length; i++) { + sum += a[i]; + r[i] = sum; + } + return r; + } + + public static Object scanAddI_loop_reassociate(int[] a, int[] r) { + int sum = 0; + int i = 0; + for (; i < a.length - 3; i += 4) { + // We cut the latency by a factor of 4, but increase the number of additions. + int old_sum = sum; + int v0 = a[i + 0]; + int v1 = a[i + 1]; + int v2 = a[i + 2]; + int v3 = a[i + 3]; + int v01 = v0 + v1; + int v23 = v2 + v3; + int v0123 = v01 + v23; + sum += v0123; + r[i + 0] = old_sum + v0; + r[i + 1] = old_sum + v01; + r[i + 2] = old_sum + v01 + v2; + r[i + 3] = old_sum + v0123; + } + for (; i < a.length; i++) { + sum += a[i]; + r[i] = sum; + } + return r; + } + + public static Object scanAddI_VectorAPI_permute_add(int[] a, int[] r) { + // Using Naive Parallel Algorithm: Hills and Steele + int sum = 0; + int xx = 0; // masked later anyway + var shf1 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, 0); + var shf2 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 0); + var shf3 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, xx, xx, xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 0); + var shf4 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, xx, xx, xx, xx, xx, xx, xx, 0, 1, 2, 3, 4, 5, 6, 7}, 0); + var mask1 = VectorMask.fromLong(SPECIES_I512, 0b1111111111111110); + var mask2 = VectorMask.fromLong(SPECIES_I512, 0b1111111111111100); + var mask3 = VectorMask.fromLong(SPECIES_I512, 0b1111111111110000); + var mask4 = VectorMask.fromLong(SPECIES_I512, 0b1111111100000000); + int i = 0; + for (; i < SPECIES_I512.loopBound(a.length); i += SPECIES_I512.length()) { + IntVector v = IntVector.fromArray(SPECIES_I512, a, i); + v = v.add(v.rearrange(shf1), mask1); + v = v.add(v.rearrange(shf2), mask2); + v = v.add(v.rearrange(shf3), mask3); + v = v.add(v.rearrange(shf4), mask4); + v = v.add(sum); + v.intoArray(r, i); + sum = v.lane(SPECIES_I512.length() - 1); + } + for (; i < a.length; i++) { + sum += a[i]; + r[i] = sum; + } + return r; + } + + public static int findMinIndexI_loop(int[] a) { + int min = a[0]; + int index = 0; + for (int i = 1; i < a.length; i++) { + int ai = a[i]; + if (ai < min) { + min = ai; + index = i; + } + } + return index; + } + + public static int findMinIndexI_VectorAPI(int[] a) { + // Main approach: have partial results in mins and idxs. + var mins = IntVector.broadcast(SPECIES_I, a[0]); + var idxs = IntVector.broadcast(SPECIES_I, 0); + var iota = IntVector.broadcast(SPECIES_I, 0).addIndex(1); + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + var mask = v.compare(VectorOperators.LT, mins); + mins = mins.blend(v, mask); + idxs = idxs.blend(iota, mask); + iota = iota.add(SPECIES_I.length()); + } + // Reduce the vectors down + int min = mins.reduceLanes(VectorOperators.MIN); + var not_min_mask = mins.compare(VectorOperators.NE, min); + int index = idxs.blend(a.length, not_min_mask).reduceLanes(VectorOperators.MIN); + // Tail loop + for (; i < a.length; i++) { + int ai = a[i]; + if (ai < min) { + min = ai; + index = i; + } + } + return index; + } + + public static int findI_loop(int[] a, int e) { + for (int i = 0; i < a.length; i++) { + int ai = a[i]; + if (ai == e) { + return i; + } + } + return -1; + } + + public static int findI_VectorAPI(int[] a, int e) { + var es = IntVector.broadcast(SPECIES_I, e); + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + var mask = v.compare(VectorOperators.EQ, es); + if (mask.anyTrue()) { + return i + mask.firstTrue(); + } + } + for (; i < a.length; i++) { + int ai = a[i]; + if (ai == e) { + return i; + } + } + return -1; + } + + public static Object reverseI_loop(int[] a, int[] r) { + for (int i = 0; i < a.length; i++) { + r[a.length - i - 1] = a[i]; + } + return r; + } + + private static final VectorShuffle REVERSE_SHUFFLE_I = SPECIES_I.iotaShuffle(SPECIES_I.length()-1, -1, true); + + public static Object reverseI_VectorAPI(int[] a, int[] r) { + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + v = v.rearrange(REVERSE_SHUFFLE_I); + v.intoArray(r, r.length - SPECIES_I.length() - i); + } + for (; i < a.length; i++) { + r[a.length - i - 1] = a[i]; + } + return r; + } + + public static Object filterI_loop(int[] a, int[] r, int threshold) { + int j = 0; + for (int i = 0; i < a.length; i++) { + int ai = a[i]; + if (ai >= threshold) { + r[j++] = ai; + } + } + // Just force the resulting length onto the same array. + r[r.length - 1] = j; + return r; + } + + public static Object filterI_VectorAPI(int[] a, int[] r, int threshold) { + var thresholds = IntVector.broadcast(SPECIES_I, threshold); + int j = 0; + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + var mask = v.compare(VectorOperators.GE, thresholds); + v = v.compress(mask); + int trueCount = mask.trueCount(); + var prefixMask = mask.compress(); + v.intoArray(r, j, prefixMask); + j += trueCount; + } + + for (; i < a.length; i++) { + int ai = a[i]; + if (ai >= threshold) { + r[j++] = ai; + } + } + // Just force the resulting length onto the same array. + r[r.length - 1] = j; + return r; + } + + // X4: ints simulate 4-byte oops. + // oops: if non-zero (= non-null), every entry simulates a 4-byte oop, pointing into mem. + // mem: an int array that simulates the memory. + // + // Task: Find all non-null oops, and dereference them, get the relevant field. + // Objects have 16 bytes, and the relevant field is at bytes 12-16. + // That maps to 4 ints, and the relevant field is the 4th element of 4. + // Sum up all the field values. + public static int reduceAddIFieldsX4_loop(int[] oops, int[] mem) { + int sum = 0; + for (int i = 0; i < oops.length; i++) { + int oop = oops[i]; + if (oop != 0) { + int fieldValue = mem[oop + 3]; // oop+12 + sum += fieldValue; + } + } + return sum; + } + + public static int reduceAddIFieldsX4_VectorAPI(int[] oops, int[] mem) { + var acc = IntVector.broadcast(SPECIES_I, 0); + int i = 0; + for (; i < SPECIES_I.loopBound(oops.length); i += SPECIES_I.length()) { + var oopv = IntVector.fromArray(SPECIES_I, oops, i); + var mask = oopv.compare(VectorOperators.NE, /* null */0); + // We are lucky today: we need to access mem[oop + 3] + var fieldValues = IntVector.fromArray(SPECIES_I, mem, 3, oops, i, mask); + acc = acc.add(fieldValues); + } + int sum = acc.reduceLanes(VectorOperators.ADD); + for (; i < oops.length; i++) { + int oop = oops[i]; + if (oop != 0) { + int fieldValue = mem[oop + 3]; // oop+12 + sum += fieldValue; + } + } + return sum; + } + + // The lowerCase example demonstrates a lane-wise control-flow diamond. + public static Object lowerCaseB_loop(byte[] a, byte[] r) { + for (int i = 0; i < a.length; i++) { + byte c = a[i]; + if (c >= 'A' && c <= 'Z') { + c += ('a' - 'A'); // c += 32 + } + r[i] = c; + } + return r; + } + + // Control-flow diamonds can easily be simulated by "if-conversion", i.e. + // by using masked operations. An alternative would be to use blend. + public static Object lowerCaseB_VectorAPI_v1(byte[] a, byte[] r) { + int i; + for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { + var vc = ByteVector.fromArray(SPECIES_B, a, i); + var maskA = vc.compare(VectorOperators.GE, (byte)'A'); + var maskZ = vc.compare(VectorOperators.LE, (byte)'Z'); + var mask = maskA.and(maskZ); + vc = vc.add((byte)32, mask); + vc.intoArray(r, i); + } + for (; i < a.length; i++) { + byte c = a[i]; + if (c >= 'A' && c <= 'Z') { + c += ('a' - 'A'); + } + r[i] = c; + } + return r; + } + + public static Object lowerCaseB_VectorAPI_v2(byte[] a, byte[] r) { + int i; + for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { + var vc = ByteVector.fromArray(SPECIES_B, a, i); + // We can convert the range 65..90 (represents ascii A..Z) into a range 0..25. + // This allows us to only use a single unsigned comparison. + var vt = vc.add((byte)-'A'); + var mask = vt.compare(VectorOperators.ULE, (byte)25); + vc = vc.add((byte)32, mask); + vc.intoArray(r, i); + } + for (; i < a.length; i++) { + byte c = a[i]; + if (c >= 'A' && c <= 'Z') { + c += ('a' - 'A'); + } + r[i] = c; + } + return r; + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java index 6cbfcbbd3c3..209eb064945 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -136,6 +136,7 @@ public class AOTMapTest { } class AOTMapTestApp { + static URLClassLoader loader; // keep Hello class alive public static void main(String[] args) throws Exception { System.out.println("Hello AOTMapTestApp"); testCustomLoader(); @@ -144,7 +145,7 @@ class AOTMapTestApp { static void testCustomLoader() throws Exception { File custJar = new File("cust.jar"); URL[] urls = new URL[] {custJar.toURI().toURL()}; - URLClassLoader loader = new URLClassLoader(urls, AOTMapTestApp.class.getClassLoader()); + loader = new URLClassLoader(urls, AOTMapTestApp.class.getClassLoader()); Class c = loader.loadClass("Hello"); System.out.println(c); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsStress.java b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsStress.java index 4d176c949c6..dc677756ffa 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsStress.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsStress.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -80,8 +80,6 @@ public class SharedStringsStress { "-Xlog:gc+region+cds", "-Xlog:gc+region=trace")); TestCommon.checkDump(dumpOutput); - dumpOutput.shouldContain("string table array (primary)"); - dumpOutput.shouldContain("string table array (secondary)"); // We could create up to 26MB of archived heap objects. Run with enough Xms to ensure // SerialGC can accommodate the archived objects during VM start up. diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait.java deleted file mode 100644 index 3a747a3e86b..00000000000 --- a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait.java +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright (c) 2001, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * 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. - */ - -/* - * @test - * @bug 4413752 8262881 - * @summary Test SuspendThread with ObjectMonitor wait. - * @requires vm.jvmti - * @library /test/lib - * @compile SuspendWithObjectMonitorWait.java - * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait - */ - -import java.io.PrintStream; - -// -// main waiter resumer -// ================= ================== =================== -// launch waiter -// waiter running -// launch resumer enter threadLock -// threadLock.wait() resumer running -// enter threadLock : wait for notify -// threadLock.notify wait finishes : -// : reenter blocks : -// suspend waiter : -// exit threadLock : : -// : : -// : : : -// notify resumer : wait finishes -// join resumer : enter threadLock -// : resume waiter -// : : exit threadLock -// : reenter threadLock : -// : resumer exits -// join waiter : -// waiter exits -// - -public class SuspendWithObjectMonitorWait { - private static final String AGENT_LIB = "SuspendWithObjectMonitorWait"; - private static final int exit_delta = 95; - - private static final int DEF_TIME_MAX = 60; // default max # secs to test - private static final int JOIN_MAX = 30; // max # secs to wait for join - - public static final int TS_INIT = 1; // initial testState - public static final int TS_WAITER_RUNNING = 2; // waiter is running - public static final int TS_RESUMER_RUNNING = 3; // resumer is running - public static final int TS_READY_TO_NOTIFY = 4; // ready to notify threadLock - public static final int TS_CALL_SUSPEND = 5; // call suspend on contender - public static final int TS_READY_TO_RESUME = 6; // ready to resume waiter - public static final int TS_CALL_RESUME = 7; // call resume on waiter - public static final int TS_WAITER_DONE = 8; // waiter has run; done - - public static Object barrierLaunch = new Object(); // controls thread launch - public static Object barrierResumer = new Object(); // controls resumer - public static Object threadLock = new Object(); // testing object - - public static long count = 0; - public static boolean printDebug = false; - public volatile static int testState; - - private static void log(String msg) { System.out.println(msg); } - - native static int suspendThread(SuspendWithObjectMonitorWaitWorker thr); - native static int wait4ContendedEnter(SuspendWithObjectMonitorWaitWorker thr); - - public static void main(String[] args) throws Exception { - try { - System.loadLibrary(AGENT_LIB); - log("Loaded library: " + AGENT_LIB); - } catch (UnsatisfiedLinkError ule) { - log("Failed to load library: " + AGENT_LIB); - log("java.library.path: " + System.getProperty("java.library.path")); - throw ule; - } - - int timeMax = 0; - if (args.length == 0) { - timeMax = DEF_TIME_MAX; - } else { - int argIndex = 0; - int argsLeft = args.length; - if (args[0].equals("-p")) { - printDebug = true; - argIndex = 1; - argsLeft--; - } - if (argsLeft == 0) { - timeMax = DEF_TIME_MAX; - } else if (argsLeft == 1) { - try { - timeMax = Integer.parseUnsignedInt(args[argIndex]); - } catch (NumberFormatException nfe) { - System.err.println("'" + args[argIndex] + - "': invalid timeMax value."); - usage(); - } - } else { - usage(); - } - } - - System.exit(run(timeMax, System.out) + exit_delta); - } - - public static void logDebug(String mesg) { - if (printDebug) { - System.err.println(Thread.currentThread().getName() + ": " + mesg); - } - } - - public static void usage() { - System.err.println("Usage: " + AGENT_LIB + " [-p][time_max]"); - System.err.println("where:"); - System.err.println(" -p ::= print debug info"); - System.err.println(" time_max ::= max looping time in seconds"); - System.err.println(" (default is " + DEF_TIME_MAX + - " seconds)"); - System.exit(1); - } - - public static int run(int timeMax, PrintStream out) { - return (new SuspendWithObjectMonitorWait()).doWork(timeMax, out); - } - - public static void checkTestState(int exp) { - if (testState != exp) { - System.err.println("Failure at " + count + " loops."); - throw new InternalError("Unexpected test state value: " - + "expected=" + exp + " actual=" + testState); - } - } - - public int doWork(int timeMax, PrintStream out) { - SuspendWithObjectMonitorWaitWorker waiter; // waiter thread - SuspendWithObjectMonitorWaitWorker resumer; // resumer thread - - System.out.println("About to execute for " + timeMax + " seconds."); - - long start_time = System.currentTimeMillis(); - while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { - count++; - testState = TS_INIT; // starting the test loop - - // launch the waiter thread - synchronized (barrierLaunch) { - waiter = new SuspendWithObjectMonitorWaitWorker("waiter"); - waiter.start(); - - while (testState != TS_WAITER_RUNNING) { - try { - barrierLaunch.wait(0); // wait until it is running - } catch (InterruptedException ex) { - } - } - } - - // launch the resumer thread - synchronized (barrierLaunch) { - resumer = new SuspendWithObjectMonitorWaitWorker("resumer", waiter); - resumer.start(); - - while (testState != TS_RESUMER_RUNNING) { - try { - barrierLaunch.wait(0); // wait until it is running - } catch (InterruptedException ex) { - } - } - } - - checkTestState(TS_RESUMER_RUNNING); - - // The waiter thread was synchronized on threadLock before it - // set TS_WAITER_RUNNING and notified barrierLaunch above so - // we cannot enter threadLock until the waiter thread calls - // threadLock.wait(). - synchronized (threadLock) { - // notify waiter thread so it can try to reenter threadLock - testState = TS_READY_TO_NOTIFY; - threadLock.notify(); - - // wait for the waiter thread to block - logDebug("before contended enter wait"); - int retCode = wait4ContendedEnter(waiter); - if (retCode != 0) { - throw new RuntimeException("error in JVMTI GetThreadState: " - + "retCode=" + retCode); - } - logDebug("done contended enter wait"); - - checkTestState(TS_READY_TO_NOTIFY); - testState = TS_CALL_SUSPEND; - logDebug("before suspend thread"); - retCode = suspendThread(waiter); - if (retCode != 0) { - throw new RuntimeException("error in JVMTI SuspendThread: " - + "retCode=" + retCode); - } - logDebug("suspended thread"); - } - - // - // At this point, all of the child threads are running - // and we can get to meat of the test: - // - // - suspended threadLock waiter (trying to reenter) - // - a threadLock enter in the resumer thread - // - resumption of the waiter thread - // - a threadLock enter in the freshly resumed waiter thread - // - - synchronized (barrierResumer) { - checkTestState(TS_CALL_SUSPEND); - - // tell resumer thread to resume waiter thread - testState = TS_READY_TO_RESUME; - barrierResumer.notify(); - - // Can't call checkTestState() here because the - // resumer thread may have already resumed the - // waiter thread. - } - - try { - resumer.join(JOIN_MAX * 1000); - if (resumer.isAlive()) { - System.err.println("Failure at " + count + " loops."); - throw new InternalError("resumer thread is stuck"); - } - waiter.join(JOIN_MAX * 1000); - if (waiter.isAlive()) { - System.err.println("Failure at " + count + " loops."); - throw new InternalError("waiter thread is stuck"); - } - } catch (InterruptedException ex) { - } - - checkTestState(TS_WAITER_DONE); - } - - System.out.println("Executed " + count + " loops in " + timeMax + - " seconds."); - - return 0; - } -} - -class SuspendWithObjectMonitorWaitWorker extends Thread { - private SuspendWithObjectMonitorWaitWorker target; // target for resume operation - - public SuspendWithObjectMonitorWaitWorker(String name) { - super(name); - } - - public SuspendWithObjectMonitorWaitWorker(String name, SuspendWithObjectMonitorWaitWorker target) { - super(name); - this.target = target; - } - - native static int resumeThread(SuspendWithObjectMonitorWaitWorker thr); - - public void run() { - SuspendWithObjectMonitorWait.logDebug("thread running"); - - // - // Launch the waiter thread: - // - grab the threadLock - // - threadLock.wait() - // - releases threadLock - // - if (getName().equals("waiter")) { - // grab threadLock before we tell main we are running - SuspendWithObjectMonitorWait.logDebug("before enter threadLock"); - synchronized(SuspendWithObjectMonitorWait.threadLock) { - SuspendWithObjectMonitorWait.logDebug("enter threadLock"); - - SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_INIT); - - synchronized(SuspendWithObjectMonitorWait.barrierLaunch) { - // tell main we are running - SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_WAITER_RUNNING; - SuspendWithObjectMonitorWait.barrierLaunch.notify(); - } - - SuspendWithObjectMonitorWait.logDebug("before wait"); - - // TS_READY_TO_NOTIFY is set after the main thread has - // entered threadLock so a spurious wakeup can't get the - // waiter thread out of this threadLock.wait(0) call: - while (SuspendWithObjectMonitorWait.testState <= SuspendWithObjectMonitorWait.TS_READY_TO_NOTIFY) { - try { - SuspendWithObjectMonitorWait.threadLock.wait(0); - } catch (InterruptedException ex) { - } - } - - SuspendWithObjectMonitorWait.logDebug("after wait"); - - SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_CALL_RESUME); - SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_WAITER_DONE; - - SuspendWithObjectMonitorWait.logDebug("exit threadLock"); - } - } - // - // Launch the resumer thread: - // - tries to grab the threadLock (should not block!) - // - grabs threadLock - // - resumes the waiter thread - // - releases threadLock - // - else if (getName().equals("resumer")) { - synchronized(SuspendWithObjectMonitorWait.barrierResumer) { - synchronized(SuspendWithObjectMonitorWait.barrierLaunch) { - // tell main we are running - SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_RESUMER_RUNNING; - SuspendWithObjectMonitorWait.barrierLaunch.notify(); - } - SuspendWithObjectMonitorWait.logDebug("thread waiting"); - while (SuspendWithObjectMonitorWait.testState != SuspendWithObjectMonitorWait.TS_READY_TO_RESUME) { - try { - // wait for main to tell us when to continue - SuspendWithObjectMonitorWait.barrierResumer.wait(0); - } catch (InterruptedException ex) { - } - } - } - - SuspendWithObjectMonitorWait.logDebug("before enter threadLock"); - synchronized(SuspendWithObjectMonitorWait.threadLock) { - SuspendWithObjectMonitorWait.logDebug("enter threadLock"); - - SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_READY_TO_RESUME); - SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_CALL_RESUME; - - // resume the waiter thread so waiter.join() can work - SuspendWithObjectMonitorWait.logDebug("before resume thread"); - int retCode = resumeThread(target); - if (retCode != 0) { - throw new RuntimeException("error in JVMTI ResumeThread: " + - "retCode=" + retCode); - } - SuspendWithObjectMonitorWait.logDebug("resumed thread"); - - SuspendWithObjectMonitorWait.logDebug("exit threadLock"); - } - } - } -} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitBase.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitBase.java new file mode 100644 index 00000000000..5443e4005fe --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitBase.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2001, 2026, 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. + * + * 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. + */ + +import java.io.PrintStream; + +public class SuspendWithObjectMonitorWaitBase { + protected static final String AGENT_LIB = "SuspendWithObjectMonitorWait"; + protected static final int exit_delta = 95; + + protected static final int DEF_TIME_MAX = 60; // default max # secs to test + protected static final int JOIN_MAX = 30; // max # secs to wait for join + + public static final int TS_INIT = 1; // initial testState + public static final int TS_WAITER_RUNNING = 2; // waiter is running + public static final int TS_RESUMER_RUNNING = 3; // resumer is running + public static final int TS_READY_TO_NOTIFY = 4; // ready to notify threadLock + public static final int TS_CALL_SUSPEND = 5; // call suspend on contender + public static final int TS_READY_TO_RESUME = 6; // ready to resume waiter + public static final int TS_CALL_RESUME = 7; // call resume on waiter + public static final int TS_WAITER_DONE = 8; // waiter has run; done + + public static Object barrierLaunch = new Object(); // controls thread launch + public static Object barrierResumer = new Object(); // controls resumer + public static Object threadLock = new Object(); // testing object + + public static long count = 0; + public static boolean printDebug = false; + public volatile static int testState; + + protected static void log(String msg) { System.out.println(msg); } + + native static int suspendThread(SuspendWithObjectMonitorWaitWorker thr); + native static int wait4ContendedEnter(SuspendWithObjectMonitorWaitWorker thr); + + public static void logDebug(String mesg) { + if (printDebug) { + System.err.println(Thread.currentThread().getName() + ": " + mesg); + } + } + + public static void usage() { + System.err.println("Usage: " + AGENT_LIB + " test_case [-p] [time_max]"); + System.err.println("where:"); + System.err.println(" test_case ::= 1 | 2 | 3"); + System.err.println(" -p ::= print debug info"); + System.err.println(" time_max ::= max looping time in seconds"); + System.err.println(" (default is " + DEF_TIME_MAX + + " seconds)"); + System.exit(1); + } + + public static void checkTestState(int exp) { + if (testState != exp) { + System.err.println("Failure at " + count + " loops."); + throw new InternalError("Unexpected test state value: " + + "expected=" + exp + " actual=" + testState); + } + } + + public SuspendWithObjectMonitorWaitWorker launchWaiter(long waitTimeout) { + SuspendWithObjectMonitorWaitWorker waiter; + // launch the waiter thread + synchronized (barrierLaunch) { + waiter = new SuspendWithObjectMonitorWaitWorker("waiter", waitTimeout); + waiter.start(); + + while (testState != TS_WAITER_RUNNING) { + try { + barrierLaunch.wait(0); // wait until it is running + } catch (InterruptedException ex) { + } + } + } + return waiter; + } + + public SuspendWithObjectMonitorWaitWorker launchResumer(SuspendWithObjectMonitorWaitWorker waiter) { + SuspendWithObjectMonitorWaitWorker resumer; + synchronized (barrierLaunch) { + resumer = new SuspendWithObjectMonitorWaitWorker("resumer", waiter); + resumer.start(); + + while (testState != TS_RESUMER_RUNNING) { + try { + barrierLaunch.wait(0); // wait until it is running + } catch (InterruptedException ex) { + } + } + } + return resumer; + } + + public void barrierResumerNotify() { + synchronized (barrierResumer) { + checkTestState(TS_CALL_SUSPEND); + + // tell resumer thread to resume waiter thread + testState = TS_READY_TO_RESUME; + barrierResumer.notify(); + + // Can't call checkTestState() here because the + // resumer thread may have already resumed the + // waiter thread. + } + } + + public void shutDown(SuspendWithObjectMonitorWaitWorker resumer, SuspendWithObjectMonitorWaitWorker waiter) { + try { + resumer.join(JOIN_MAX * 1000); + if (resumer.isAlive()) { + System.err.println("Failure at " + count + " loops."); + throw new InternalError("resumer thread is stuck"); + } + waiter.join(JOIN_MAX * 1000); + if (waiter.isAlive()) { + System.err.println("Failure at " + count + " loops."); + throw new InternalError("waiter thread is stuck"); + } + } catch (InterruptedException ex) { + } + } + + public int run(int timeMax, PrintStream out) { + return 0; + } + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + System.err.println("Invalid number of arguments, there should be at least a test_case given."); + usage(); + } + + if (args.length > 3) { + System.err.println("Invalid number of arguments, there are too many arguments."); + usage(); + } + + try { + System.loadLibrary(AGENT_LIB); + log("Loaded library: " + AGENT_LIB); + } catch (UnsatisfiedLinkError ule) { + log("Failed to load library: " + AGENT_LIB); + log("java.library.path: " + System.getProperty("java.library.path")); + throw ule; + } + + int testCase = 0; + int timeMax = 0; + for (int argIndex = 0; argIndex < args.length; argIndex++) { + if (args[argIndex].equals("-p")) { + // Handle optional -p arg regardless of position. + printDebug = true; + continue; + } + + if (testCase == 0) { + try { + // testCase must be the first non-optional arg. + testCase = Integer.parseUnsignedInt(args[argIndex]); + log("testCase = " + testCase); + } catch (NumberFormatException nfe) { + System.err.println("'" + args[argIndex] + + "': invalid test_case value."); + usage(); + } + if (testCase < 1 || testCase > 3) { + System.err.println("Invalid test_case value: '" + testCase + "'"); + usage(); + } + continue; + } + + if (argIndex < args.length) { + // timeMax is an optional arg. + try { + timeMax = Integer.parseUnsignedInt(args[argIndex]); + } catch (NumberFormatException nfe) { + System.err.println("'" + args[argIndex] + + "': invalid time_max value."); + usage(); + } + } else { + timeMax = DEF_TIME_MAX; + } + } + + if (timeMax == 0) { + timeMax = DEF_TIME_MAX; + } + log("timeMax = " + timeMax); + + if (testCase == 0) { + // Just -p was given. + System.err.println("Invalid number of arguments, no test_case given."); + usage(); + } + + SuspendWithObjectMonitorWaitBase test = null; + switch (testCase) { + case 1: + test = new SuspendWithObjectMonitorWaitDefault(); + break; + case 2: + test = new SuspendWithObjectMonitorWaitReentryPartFirst(); + break; + case 3: + test = new SuspendWithObjectMonitorWaitReentryPartSecond(); + break; + default: + // Impossible + break; + } + int result = test.run(timeMax, System.out); + System.exit(result + exit_delta); + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitDefault.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitDefault.java new file mode 100644 index 00000000000..b2c1f108de5 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitDefault.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2001, 2026, 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. + * + * 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. + */ + +/* + * @test + * @bug 4413752 8262881 + * @summary Test SuspendThread with ObjectMonitor wait. + * @requires vm.jvmti + * @library /test/lib + * @compile SuspendWithObjectMonitorWaitDefault.java + * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWaitDefault 1 + */ + +import java.io.PrintStream; + +// +// SuspendWithObjectMonitorWaitDefault algorithm: +// +// main waiter resumer +// ================= ================== =================== +// launch waiter +// waiter running +// launch resumer enter threadLock +// threadLock.wait() resumer running +// enter threadLock : wait for notify +// threadLock.notify wait finishes : +// : reenter blocks : +// suspend waiter : +// exit threadLock : : +// : : +// : : : +// notify resumer : wait finishes +// join resumer : enter threadLock +// : resume waiter +// : : exit threadLock +// : reenter threadLock : +// : resumer exits +// join waiter : +// waiter exits +// + +public class SuspendWithObjectMonitorWaitDefault extends SuspendWithObjectMonitorWaitBase { + + @Override + public int run(int timeMax, PrintStream out) { + return doWork1(timeMax, out); + } + + // Default scenario, the resumer thread is always able to grab the threadLock once notified by the main thread. + public int doWork1(int timeMax, PrintStream out) { + SuspendWithObjectMonitorWaitWorker waiter; // waiter thread + SuspendWithObjectMonitorWaitWorker resumer; // resumer thread + + System.out.println("Test 1: About to execute for " + timeMax + " seconds."); + + long start_time = System.currentTimeMillis(); + while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { + count++; + testState = TS_INIT; // starting the test loop + + // launch the waiter thread + waiter = launchWaiter(0); + + // launch the resumer thread + resumer = launchResumer(waiter); + + checkTestState(TS_RESUMER_RUNNING); + + // The waiter thread was synchronized on threadLock before it + // set TS_WAITER_RUNNING and notified barrierLaunch above so + // we cannot enter threadLock until the waiter thread calls + // threadLock.wait(). + synchronized (threadLock) { + // notify waiter thread so it can try to reenter threadLock + testState = TS_READY_TO_NOTIFY; + threadLock.notify(); + + // wait for the waiter thread to block + logDebug("before contended enter wait"); + int retCode = wait4ContendedEnter(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI GetThreadState: " + + "retCode=" + retCode); + } + logDebug("done contended enter wait"); + + checkTestState(TS_READY_TO_NOTIFY); + testState = TS_CALL_SUSPEND; + logDebug("before suspend thread"); + retCode = suspendThread(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI SuspendThread: " + + "retCode=" + retCode); + } + logDebug("suspended thread"); + } + + // + // At this point, all of the child threads are running + // and we can get to meat of the test: + // + // - suspended threadLock waiter (trying to reenter) + // - a threadLock enter in the resumer thread + // - resumption of the waiter thread + // - a threadLock enter in the freshly resumed waiter thread + // + barrierResumerNotify(); + + shutDown(waiter ,resumer); + checkTestState(TS_WAITER_DONE); + } + + System.out.println("Executed " + count + " loops in " + timeMax + + " seconds."); + + return 0; + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitReentryPartFirst.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitReentryPartFirst.java new file mode 100644 index 00000000000..b331b338f47 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitReentryPartFirst.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2001, 2026, 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. + * + * 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. + */ + +/* + * @test + * @bug 4413752 8262881 + * @summary Test SuspendThread with ObjectMonitor wait. + * @requires vm.jvmti + * @library /test/lib + * @compile SuspendWithObjectMonitorWaitReentryPartFirst.java + * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWaitReentryPartFirst 2 + */ + +import java.io.PrintStream; + +// +// SuspendWithObjectMonitorWaitReentryPartFirst algorithm: +// +// main waiter resumer +// ================= ================== =================== +// launch waiter +// waiter running +// launch resumer enter threadLock +// threadLock.wait() resumer running +// enter threadLock : wait for notify +// threadLock.notify wait finishes : +// : reenter blocks : +// suspend waiter : +// : : +// : : : +// notify resumer : wait finishes +// delay 1-second : : +// exit threadLock : : +// join resumer : enter threadLock +// : resume waiter +// : : exit threadLock +// : reenter threadLock : +// : resumer exits +// join waiter : +// waiter exits +// +// Note: The sleep(1-second) in main along with the delayed exit +// of threadLock in main forces the resumer thread to reach +// "enter threadLock" and block. This difference from the default scenario +// forces the resumer thread to be contending for threadLock +// while the waiter thread is in threadLock.wait() increasing +// stress on the monitor sub-system. +// + +public class SuspendWithObjectMonitorWaitReentryPartFirst extends SuspendWithObjectMonitorWaitBase { + + @Override + public int run(int timeMax, PrintStream out) { + return doWork2(timeMax, out); + } + + // Notify the resumer while holding the threadLock. + public int doWork2(int timeMax, PrintStream out) { + SuspendWithObjectMonitorWaitWorker waiter; // waiter thread + SuspendWithObjectMonitorWaitWorker resumer; // resumer thread + + System.out.println("Test 2: About to execute for " + timeMax + " seconds."); + + long start_time = System.currentTimeMillis(); + while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { + count++; + testState = TS_INIT; // starting the test loop + + // launch the waiter thread + waiter = launchWaiter(0); + + // launch the resumer thread + resumer = launchResumer(waiter); + + checkTestState(TS_RESUMER_RUNNING); + + // The waiter thread was synchronized on threadLock before it + // set TS_WAITER_RUNNING and notified barrierLaunch above so + // we cannot enter threadLock until the waiter thread calls + // threadLock.wait(). + synchronized (threadLock) { + // notify waiter thread so it can try to reenter threadLock + testState = TS_READY_TO_NOTIFY; + threadLock.notify(); + + // wait for the waiter thread to block + logDebug("before contended enter wait"); + int retCode = wait4ContendedEnter(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI GetThreadState: " + + "retCode=" + retCode); + } + logDebug("done contended enter wait"); + + checkTestState(TS_READY_TO_NOTIFY); + testState = TS_CALL_SUSPEND; + logDebug("before suspend thread"); + retCode = suspendThread(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI SuspendThread: " + + "retCode=" + retCode); + } + logDebug("suspended thread"); + + // + // At this point, all of the child threads are running + // and we can get to meat of the test: + // + // - suspended threadLock waiter (trying to reenter) + // - a blocked threadLock enter in the resumer thread while the + // threadLock is held by the main thread. + // - resumption of the waiter thread + // - a threadLock enter in the freshly resumed waiter thread + // + barrierResumerNotify(); + try { + // Delay for 1-second while holding the threadLock to force the + // resumer thread to block on entering the threadLock. + Thread.sleep(1000); + } catch (Exception e) {} + } + + shutDown(waiter ,resumer); + checkTestState(TS_WAITER_DONE); + } + + System.out.println("Executed " + count + " loops in " + timeMax + + " seconds."); + + return 0; + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitReentryPartSecond.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitReentryPartSecond.java new file mode 100644 index 00000000000..92ab5a88eb6 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitReentryPartSecond.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2001, 2026, 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. + * + * 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. + */ + +/* + * @test + * @bug 4413752 8262881 + * @summary Test SuspendThread with ObjectMonitor wait. + * @requires vm.jvmti + * @library /test/lib + * @compile SuspendWithObjectMonitorWaitReentryPartSecond.java + * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWaitReentryPartSecond 3 + */ + +import java.io.PrintStream; + +// +// SuspendWithObjectMonitorWaitReentryPartSecond algorithm: +// +// main waiter resumer +// =================== ====================== =================== +// launch waiter +// waiter running +// launch resumer enter threadLock +// while !READY_TO_NOTIFY resumer running +// : threadLock.wait(1) wait for notify +// enter threadLock : : +// set READY_TO_NOTIFY : +// threadLock.notify wait finishes : +// : delay 200ms reenter blocks : +// suspend waiter : +// : : +// : : : +// notify resumer : wait finishes +// delay 1-second : : +// exit threadLock : : +// join resumer : enter threadLock +// : resume waiter +// : : exit threadLock +// : reenter threadLock : +// : resumer exits +// join waiter : +// waiter exits +// +// Note: The sleep(1-second) in main along with the delayed exit +// of threadLock in main forces the resumer thread to reach +// "enter threadLock" and block. This difference from the default scenario +// forces the resumer thread to be contending for threadLock +// while the waiter thread is in the threadLock.wait(1) tight +// loop increasing stress on the monitor sub-system. +// +// Note: sleep(200ms) here while holding the threadLock to allow the +// waiter thread's timed wait to finish before we attempt to +// suspend the waiter thread. +// + +public class SuspendWithObjectMonitorWaitReentryPartSecond extends SuspendWithObjectMonitorWaitBase { + + @Override + public int run(int timeMax, PrintStream out) { + return doWork3(timeMax, out); + } + + // Suspend on the re-entry path of wait. + public int doWork3(int timeMax, PrintStream out) { + SuspendWithObjectMonitorWaitWorker waiter; // waiter thread + SuspendWithObjectMonitorWaitWorker resumer; // resumer thread + + System.out.println("Test 3: About to execute for " + timeMax + " seconds."); + + long start_time = System.currentTimeMillis(); + while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { + count++; + testState = TS_INIT; // starting the test loop + + // launch the waiter thread + waiter = launchWaiter(100); + + // launch the resumer thread + resumer = launchResumer(waiter); + + checkTestState(TS_RESUMER_RUNNING); + + // The waiter thread was synchronized on threadLock before it + // set TS_WAITER_RUNNING and notified barrierLaunch above so + // we cannot enter threadLock until the waiter thread calls + // threadLock.wait(). + synchronized (threadLock) { + // notify waiter thread so it can try to reenter threadLock + testState = TS_READY_TO_NOTIFY; + threadLock.notify(); + + try { + Thread.sleep(200); + } catch (Exception e) {} + + // wait for the waiter thread to block + logDebug("before contended enter wait"); + int retCode = wait4ContendedEnter(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI GetThreadState: " + + "retCode=" + retCode); + } + logDebug("done contended enter wait"); + + checkTestState(TS_READY_TO_NOTIFY); + testState = TS_CALL_SUSPEND; + logDebug("before suspend thread"); + retCode = suspendThread(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI SuspendThread: " + + "retCode=" + retCode); + } + logDebug("suspended thread"); + + // + // At this point, all of the child threads are running + // and we can get to meat of the test: + // + // - suspended threadLock waiter (trying to reenter) + // - a blocked threadLock enter in the resumer thread while the + // threadLock is held by the main thread. + // - resumption of the waiter thread + // - a threadLock enter in the freshly resumed waiter thread + // + barrierResumerNotify(); + try { + // Delay for 1-second while holding the threadLock to force the + // resumer thread to block on entering the threadLock. + Thread.sleep(1000); + } catch (Exception e) {} + } + + shutDown(waiter ,resumer); + checkTestState(TS_WAITER_DONE); + } + + System.out.println("Executed " + count + " loops in " + timeMax + + " seconds."); + + return 0; + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitWorker.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitWorker.java new file mode 100644 index 00000000000..2bf41168d5a --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitWorker.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2001, 2026, 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. + * + * 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. + */ + +public class SuspendWithObjectMonitorWaitWorker extends Thread { + private SuspendWithObjectMonitorWaitWorker target; // target for resume operation + private final long waitTimeout; + + public SuspendWithObjectMonitorWaitWorker(String name, long waitTimeout) { + super(name); + this.waitTimeout = waitTimeout; + } + + public SuspendWithObjectMonitorWaitWorker(String name, SuspendWithObjectMonitorWaitWorker target) { + super(name); + this.target = target; + this.waitTimeout = 0; + } + + native static int resumeThread(SuspendWithObjectMonitorWaitWorker thr); + + public void run() { + SuspendWithObjectMonitorWaitBase.logDebug("thread running"); + + // + // Launch the waiter thread: + // - grab the threadLock + // - threadLock.wait() + // - releases threadLock + // + if (getName().equals("waiter")) { + // grab threadLock before we tell main we are running + SuspendWithObjectMonitorWaitBase.logDebug("before enter threadLock"); + synchronized(SuspendWithObjectMonitorWaitBase.threadLock) { + SuspendWithObjectMonitorWaitBase.logDebug("enter threadLock"); + + SuspendWithObjectMonitorWaitBase.checkTestState(SuspendWithObjectMonitorWaitBase.TS_INIT); + + synchronized(SuspendWithObjectMonitorWaitBase.barrierLaunch) { + // tell main we are running + SuspendWithObjectMonitorWaitBase.testState = SuspendWithObjectMonitorWaitBase.TS_WAITER_RUNNING; + SuspendWithObjectMonitorWaitBase.barrierLaunch.notify(); + } + + SuspendWithObjectMonitorWaitBase.logDebug("before wait"); + + // TS_READY_TO_NOTIFY is set after the main thread has + // entered threadLock so a spurious wakeup can't get the + // waiter thread out of this threadLock.wait(0) call: + while (SuspendWithObjectMonitorWaitBase.testState <= SuspendWithObjectMonitorWaitBase.TS_READY_TO_NOTIFY) { + try { + SuspendWithObjectMonitorWaitBase.threadLock.wait(waitTimeout); + } catch (InterruptedException ex) { + } + } + + SuspendWithObjectMonitorWaitBase.logDebug("after wait"); + + SuspendWithObjectMonitorWaitBase.checkTestState(SuspendWithObjectMonitorWaitBase.TS_CALL_RESUME); + SuspendWithObjectMonitorWaitBase.testState = SuspendWithObjectMonitorWaitBase.TS_WAITER_DONE; + + SuspendWithObjectMonitorWaitBase.logDebug("exit threadLock"); + } + } + // + // Launch the resumer thread: + // - tries to grab the threadLock (should not block with doWork1!) + // - grabs threadLock + // - resumes the waiter thread + // - releases threadLock + // + else if (getName().equals("resumer")) { + synchronized(SuspendWithObjectMonitorWaitBase.barrierResumer) { + synchronized(SuspendWithObjectMonitorWaitBase.barrierLaunch) { + // tell main we are running + SuspendWithObjectMonitorWaitBase.testState = SuspendWithObjectMonitorWaitBase.TS_RESUMER_RUNNING; + SuspendWithObjectMonitorWaitBase.barrierLaunch.notify(); + } + SuspendWithObjectMonitorWaitBase.logDebug("thread waiting"); + while (SuspendWithObjectMonitorWaitBase.testState != SuspendWithObjectMonitorWaitBase.TS_READY_TO_RESUME) { + try { + // wait for main to tell us when to continue + SuspendWithObjectMonitorWaitBase.barrierResumer.wait(0); + } catch (InterruptedException ex) { + } + } + } + + SuspendWithObjectMonitorWaitBase.logDebug("before enter threadLock"); + synchronized(SuspendWithObjectMonitorWaitBase.threadLock) { + SuspendWithObjectMonitorWaitBase.logDebug("enter threadLock"); + + SuspendWithObjectMonitorWaitBase.checkTestState(SuspendWithObjectMonitorWaitBase.TS_READY_TO_RESUME); + SuspendWithObjectMonitorWaitBase.testState = SuspendWithObjectMonitorWaitBase.TS_CALL_RESUME; + + // resume the waiter thread so waiter.join() can work + SuspendWithObjectMonitorWaitBase.logDebug("before resume thread"); + int retCode = resumeThread(target); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI ResumeThread: " + + "retCode=" + retCode); + } + SuspendWithObjectMonitorWaitBase.logDebug("resumed thread"); + + SuspendWithObjectMonitorWaitBase.logDebug("exit threadLock"); + } + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/libSuspendWithObjectMonitorWait.cpp b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/libSuspendWithObjectMonitorWait.cpp index 3706cba76dc..bea70c4925f 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/libSuspendWithObjectMonitorWait.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/libSuspendWithObjectMonitorWait.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -36,7 +36,7 @@ static jvmtiEnv* jvmti = nullptr; } while (0) JNIEXPORT int JNICALL -Java_SuspendWithObjectMonitorWait_suspendThread(JNIEnv *jni, jclass cls, jthread thr) { +Java_SuspendWithObjectMonitorWaitBase_suspendThread(JNIEnv *jni, jclass cls, jthread thr) { return jvmti->SuspendThread(thr); } @@ -46,7 +46,7 @@ Java_SuspendWithObjectMonitorWaitWorker_resumeThread(JNIEnv *jni, jclass cls, jt } JNIEXPORT jint JNICALL -Java_SuspendWithObjectMonitorWait_wait4ContendedEnter(JNIEnv *jni, jclass cls, jthread thr) { +Java_SuspendWithObjectMonitorWaitBase_wait4ContendedEnter(JNIEnv *jni, jclass cls, jthread thr) { jvmtiError err; jint thread_state; do { diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java index b8b19c743e9..9fc304d1854 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java @@ -30,9 +30,11 @@ import jdk.test.lib.apps.LingeredApp; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.util.CoreUtils; +import jtreg.SkippedException; + /** * @test - * @bug 8374482 + * @bug 8374482 8376284 * @requires (os.family == "linux") & (vm.hasSA) * @requires os.arch == "amd64" * @library /test/lib @@ -59,11 +61,21 @@ public class TestJhsdbJstackMixedCore { System.out.println(out.getStdout()); System.err.println(out.getStderr()); - out.shouldContain(""); + out.shouldContain("__restore_rt "); out.shouldContain("Java_jdk_test_lib_apps_LingeredApp_crash"); } public static void main(String... args) throws Throwable { + // Check whether the symbol of signal trampoline is available. + var libc = SATestUtils.getLibCPath(); + + // SA distinguishes the frame is signal trampoline if the function + // is named "__restore_rt". + // SA cannot unwind problematic frame from it if the symbol not found. + if (!SATestUtils.isSymbolAvailable(libc, "__restore_rt")) { + throw new SkippedException("Signal trampoline (__restore_rt) not found in libc."); + } + LingeredApp app = new LingeredApp(); app.setForceCrash(true); LingeredApp.startApp(app, CoreUtils.getAlwaysPretouchArg(true)); diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBadFormat.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBadFormat.java index 200866375af..da8fd6489b8 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBadFormat.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBadFormat.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -276,9 +276,10 @@ class BadArgumentsAnnotation { } } +// Since all the methods are failing, the class doesn't specify any @Test methods, which is another failure. +@ClassFail class BadOverloadedMethod { - @FailCount(0) // Combined with both sameName() below @Test public void sameName() {} diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBasics.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBasics.java index b5006290047..8efdcb4f021 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBasics.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBasics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -28,6 +28,8 @@ import compiler.lib.ir_framework.test.TestVM; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.stream.Stream; /* @@ -44,7 +46,7 @@ import java.util.stream.Stream; public class TestBasics { private static boolean wasExecuted = false; private boolean lastToggleBoolean = true; - private final static int[] executed = new int[100]; + private final static HashMap executed = HashMap.newHashMap(100); private final static int[] executedOnce = new int[5]; private long[] nonFloatingRandomNumbers = new long[10]; private double[] floatingRandomNumbers = new double[10]; @@ -60,12 +62,12 @@ public class TestBasics { if (wasExecuted) { throw new RuntimeException("Executed non @Test method or a method that was not intended to be run"); } - for (int i = 0; i < executed.length; i++) { - int value = executed[i]; + for (Map.Entry entry : executed.entrySet()) { + int value = entry.getValue(); if (value != TestVM.WARMUP_ITERATIONS + 1) { // Warmups + 1 C2 compiled invocation - throw new RuntimeException("Test " + i + " was executed " + value + " times instead stead of " - + (TestVM.WARMUP_ITERATIONS + 1) + " times." ); + throw new RuntimeException("Test " + entry.getKey() + " was executed " + value + " times instead stead of " + + (TestVM.WARMUP_ITERATIONS + 1) + " times." ); } } @@ -88,12 +90,6 @@ public class TestBasics { randomBooleans = new Boolean[64]; } - // Base test, no arguments, directly invoked. - @Test - public void test() { - executed[0]++; - } - // Not a test public void noTest() { wasExecuted = true; @@ -109,24 +105,19 @@ public class TestBasics { wasExecuted = true; } - // Can overload a @Test if it is not a @Test itself. - public static void test(double i) { - wasExecuted = true; - } - @Test public static void staticTest() { - executed[1]++; + executed.merge("staticTest", 1, Integer::sum); } @Test public final void finalTest() { - executed[2]++; + executed.merge("finalTest", 1, Integer::sum); } @Test public int returnValueTest() { - executed[3]++; + executed.merge("returnValueTest", 1, Integer::sum); return 4; } @@ -135,7 +126,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void byteDefaultArgument(byte x) { - executed[4]++; + executed.merge("byteDefaultArgument", 1, Integer::sum); if (x != 0) { throw new RuntimeException("Must be 0"); } @@ -144,7 +135,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void shortDefaultArgument(short x) { - executed[5]++; + executed.merge("shortDefaultArgument", 1, Integer::sum); if (x != 0) { throw new RuntimeException("Must be 0"); } @@ -153,7 +144,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void intDefaultArgument(int x) { - executed[6]++; + executed.merge("intDefaultArgument", 1, Integer::sum); if (x != 0) { throw new RuntimeException("Must be 0"); } @@ -162,7 +153,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void longDefaultArgument(long x) { - executed[7]++; + executed.merge("longDefaultArgument", 1, Integer::sum); if (x != 0L) { throw new RuntimeException("Must be 0"); } @@ -171,7 +162,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void floatDefaultArgument(float x) { - executed[8]++; + executed.merge("floatDefaultArgument", 1, Integer::sum); if (x != 0.0f) { throw new RuntimeException("Must be 0.0"); } @@ -180,7 +171,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void doubleDefaultArgument(double x) { - executed[9]++; + executed.merge("doubleDefaultArgument", 1, Integer::sum); if (x != 0.0f) { throw new RuntimeException("Must be 0.0"); } @@ -189,7 +180,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void charDefaultArgument(char x) { - executed[10]++; + executed.merge("charDefaultArgument", 1, Integer::sum); if (x != '\u0000') { throw new RuntimeException("Must be \u0000"); } @@ -198,7 +189,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void booleanDefaultArgument(boolean x) { - executed[11]++; + executed.merge("booleanDefaultArgument", 1, Integer::sum); if (x) { throw new RuntimeException("Must be false"); } @@ -207,7 +198,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void stringObjectDefaultArgument(String x) { - executed[12]++; + executed.merge("stringObjectDefaultArgument", 1, Integer::sum); if (x == null || x.length() != 0) { throw new RuntimeException("Default string object must be non-null and having a length of zero"); } @@ -216,7 +207,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void defaultObjectDefaultArgument(DefaultObject x) { - executed[13]++; + executed.merge("defaultObjectDefaultArgument", 1, Integer::sum); if (x == null || x.i != 4) { throw new RuntimeException("Default object must not be null and its i field must be 4"); } @@ -225,7 +216,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_42) public void byte42(byte x) { - executed[14]++; + executed.merge("byte42", 1, Integer::sum); if (x != 42) { throw new RuntimeException("Must be 42"); } @@ -234,7 +225,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_42) public void short42(short x) { - executed[15]++; + executed.merge("short42", 1, Integer::sum); if (x != 42) { throw new RuntimeException("Must be 42"); } @@ -243,7 +234,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_42) public void int42(int x) { - executed[16]++; + executed.merge("int42", 1, Integer::sum); if (x != 42) { throw new RuntimeException("Must be 42"); } @@ -252,7 +243,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_42) public void long42(long x) { - executed[17]++; + executed.merge("long42", 1, Integer::sum); if (x != 42) { throw new RuntimeException("Must be 42"); } @@ -261,7 +252,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_42) public void float42(float x) { - executed[18]++; + executed.merge("float42", 1, Integer::sum); if (x != 42.0) { throw new RuntimeException("Must be 42"); } @@ -270,7 +261,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_42) public void double42(double x) { - executed[19]++; + executed.merge("double42", 1, Integer::sum); if (x != 42.0) { throw new RuntimeException("Must be 42"); } @@ -279,7 +270,7 @@ public class TestBasics { @Test @Arguments(values = Argument.FALSE) public void booleanFalse(boolean x) { - executed[20]++; + executed.merge("booleanFalse", 1, Integer::sum); if (x) { throw new RuntimeException("Must be false"); } @@ -288,7 +279,7 @@ public class TestBasics { @Test @Arguments(values = Argument.TRUE) public void booleanTrue(boolean x) { - executed[21]++; + executed.merge("booleanTrue", 1, Integer::sum); if (!x) { throw new RuntimeException("Must be true"); } @@ -297,37 +288,37 @@ public class TestBasics { @Test @Arguments(values = Argument.RANDOM_ONCE) public void randomByte(byte x) { - executed[22]++; + executed.merge("randomByte", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_ONCE) public void randomShort(short x) { - executed[23]++; + executed.merge("randomShort", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_ONCE) public void randomInt(int x) { - executed[24]++; + executed.merge("randomInt", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_ONCE) public void randomLong(long x) { - executed[25]++; + executed.merge("randomLong", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_ONCE) public void randomFloat(float x) { - executed[26]++; + executed.merge("randomFloat", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_ONCE) public void randomDouble(double x) { - executed[27]++; + executed.merge("randomDouble", 1, Integer::sum); } // Not executed @@ -338,13 +329,13 @@ public class TestBasics { @Test @Arguments(values = Argument.RANDOM_ONCE) public void randomBoolean(boolean x) { - executed[28]++; + executed.merge("randomBoolean", 1, Integer::sum); } @Test @Arguments(values = Argument.BOOLEAN_TOGGLE_FIRST_FALSE) public void booleanToggleFirstFalse(boolean x) { - if (executed[29] == 0) { + if (!executed.containsKey("booleanToggleFirstFalse")) { // First invocation if (x) { throw new RuntimeException("BOOLEAN_TOGGLE_FIRST_FALSE must be false on first invocation"); @@ -353,63 +344,63 @@ public class TestBasics { throw new RuntimeException("BOOLEAN_TOGGLE_FIRST_FALSE did not toggle"); } lastToggleBoolean = x; - executed[29]++; + executed.merge("booleanToggleFirstFalse", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachByte(byte x) { - checkNonFloatingRandomNumber(x, executed[30]); - executed[30]++; + checkNonFloatingRandomNumber(x, executed.getOrDefault("randomEachByte", 0)); + executed.merge("randomEachByte", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachShort(short x) { - checkNonFloatingRandomNumber(x, executed[31]); - executed[31]++; + checkNonFloatingRandomNumber(x, executed.getOrDefault("randomEachShort", 0)); + executed.merge("randomEachShort", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachInt(int x) { - checkNonFloatingRandomNumber(x, executed[32]); - executed[32]++; + checkNonFloatingRandomNumber(x, executed.getOrDefault("randomEachInt", 0)); + executed.merge("randomEachInt", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachLong(long x) { - checkNonFloatingRandomNumber(x, executed[33]); - executed[33]++; + checkNonFloatingRandomNumber(x, executed.getOrDefault("randomEachLong", 0)); + executed.merge("randomEachLong", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachChar(char x) { - checkNonFloatingRandomNumber(x, executed[34]); - executed[34]++; + checkNonFloatingRandomNumber(x, executed.getOrDefault("randomEachChar", 0)); + executed.merge("randomEachChar", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachFloat(float x) { - checkFloatingRandomNumber(x, executed[35]); - executed[35]++; + checkFloatingRandomNumber(x, executed.getOrDefault("randomEachFloat", 0)); + executed.merge("randomEachFloat", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachDouble(double x) { - checkFloatingRandomNumber(x, executed[36]); - executed[36]++; + checkFloatingRandomNumber(x, executed.getOrDefault("randomEachDouble", 0)); + executed.merge("randomEachDouble", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachBoolean(boolean x) { - checkRandomBoolean(x, executed[37]); - executed[37]++; + checkRandomBoolean(x, executed.getOrDefault("randomEachBoolean", 0)); + executed.merge("randomEachBoolean", 1, Integer::sum); } private void checkNonFloatingRandomNumber(long x, int invocationCount) { @@ -461,7 +452,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_MINUS_42) public void byteMinus42(byte x) { - executed[38]++; + executed.merge("byteMinus42", 1, Integer::sum); if (x != -42) { throw new RuntimeException("Must be -42"); } @@ -470,7 +461,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_MINUS_42) public void shortMinus42(short x) { - executed[39]++; + executed.merge("shortMinus42", 1, Integer::sum); if (x != -42) { throw new RuntimeException("Must be -42"); } @@ -479,7 +470,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_MINUS_42) public void intMinus42(int x) { - executed[40]++; + executed.merge("intMinus42", 1, Integer::sum); if (x != -42) { throw new RuntimeException("Must be -42"); } @@ -488,7 +479,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_MINUS_42) public void longMinus42(long x) { - executed[41]++; + executed.merge("longMinus42", 1, Integer::sum); if (x != -42) { throw new RuntimeException("Must be -42"); } @@ -497,7 +488,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_MINUS_42) public void floatMinus42(float x) { - executed[42]++; + executed.merge("floatMinus42", 1, Integer::sum); if (x != -42.0) { throw new RuntimeException("Must be -42"); } @@ -506,7 +497,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_MINUS_42) public void doubleMinus42(double x) { - executed[43]++; + executed.merge("doubleMinus42", 1, Integer::sum); if (x != -42.0) { throw new RuntimeException("Must be -42"); } @@ -515,7 +506,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MIN) public void byteMin(byte x) { - executed[79]++; + executed.merge("byteMin", 1, Integer::sum); if (x != Byte.MIN_VALUE) { throw new RuntimeException("Must be MIN_VALUE"); } @@ -524,7 +515,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MIN) public void charMin(char x) { - executed[80]++; + executed.merge("charMin", 1, Integer::sum); if (x != Character.MIN_VALUE) { throw new RuntimeException("Must be MIN_VALUE"); } @@ -533,7 +524,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MIN) public void shortMin(short x) { - executed[81]++; + executed.merge("shortMin", 1, Integer::sum); if (x != Short.MIN_VALUE) { throw new RuntimeException("Must be MIN_VALUE"); } @@ -542,7 +533,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MIN) public void intMin(int x) { - executed[82]++; + executed.merge("intMin", 1, Integer::sum); if (x != Integer.MIN_VALUE) { throw new RuntimeException("Must be MIN_VALUE"); } @@ -551,7 +542,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MIN) public void longMin(long x) { - executed[83]++; + executed.merge("longMin", 1, Integer::sum); if (x != Long.MIN_VALUE) { throw new RuntimeException("Must be MIN_VALUE"); } @@ -560,7 +551,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MIN) public void floatMin(float x) { - executed[84]++; + executed.merge("floatMin", 1, Integer::sum); if (x != Float.MIN_VALUE) { throw new RuntimeException("Must be MIN_VALUE"); } @@ -569,7 +560,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MIN) public void doubleMin(double x) { - executed[85]++; + executed.merge("doubleMin", 1, Integer::sum); if (x != Double.MIN_VALUE) { throw new RuntimeException("Must be MIN_VALUE"); } @@ -578,7 +569,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MAX) public void byteMax(byte x) { - executed[86]++; + executed.merge("byteMax", 1, Integer::sum); if (x != Byte.MAX_VALUE) { throw new RuntimeException("Must be MAX_VALUE"); } @@ -587,7 +578,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MAX) public void charMax(char x) { - executed[87]++; + executed.merge("charMax", 1, Integer::sum); if (x != Character.MAX_VALUE) { throw new RuntimeException("Must be MAX_VALUE"); } @@ -596,7 +587,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MAX) public void shortMax(short x) { - executed[88]++; + executed.merge("shortMax", 1, Integer::sum); if (x != Short.MAX_VALUE) { throw new RuntimeException("Must be MAX_VALUE"); } @@ -605,7 +596,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MAX) public void intMax(int x) { - executed[89]++; + executed.merge("intMax", 1, Integer::sum); if (x != Integer.MAX_VALUE) { throw new RuntimeException("Must be MAX_VALUE"); } @@ -614,7 +605,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MAX) public void longMax(long x) { - executed[90]++; + executed.merge("longMax", 1, Integer::sum); if (x != Long.MAX_VALUE) { throw new RuntimeException("Must be MAX_VALUE"); } @@ -623,7 +614,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MAX) public void floatMax(float x) { - executed[91]++; + executed.merge("floatMax", 1, Integer::sum); if (x != Float.MAX_VALUE) { throw new RuntimeException("Must be MAX_VALUE"); } @@ -632,7 +623,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MAX) public void doubleMax(double x) { - executed[78]++; + executed.merge("doubleMax", 1, Integer::sum); if (x != Double.MAX_VALUE) { throw new RuntimeException("Must be MAX_VALUE"); } @@ -641,7 +632,7 @@ public class TestBasics { @Test @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) public void twoArgsDefault1(byte x, short y) { - executed[44]++; + executed.merge("twoArgsDefault1", 1, Integer::sum); if (x != 0 || y != 0) { throw new RuntimeException("Both must be 0"); } @@ -650,7 +641,7 @@ public class TestBasics { @Test @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) public void twoArgsDefault2(int x, short y) { - executed[45]++; + executed.merge("twoArgsDefault2", 1, Integer::sum); if (x != 0 || y != 0) { throw new RuntimeException("Both must be 0"); } @@ -659,7 +650,7 @@ public class TestBasics { @Test @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) public void twoArgsDefault3(short x, long y) { - executed[46]++; + executed.merge("twoArgsDefault3", 1, Integer::sum); if (x != 0 || y != 0) { throw new RuntimeException("Both must be 0"); } @@ -668,7 +659,7 @@ public class TestBasics { @Test @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) public void twoArgsDefault4(float x, boolean y) { - executed[47]++; + executed.merge("twoArgsDefault4", 1, Integer::sum); if (x != 0.0 || y) { throw new RuntimeException("Must be 0 and false"); } @@ -677,7 +668,7 @@ public class TestBasics { @Test @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) public void twoArgsDefault5(boolean x, char y) { - executed[48]++; + executed.merge("twoArgsDefault5", 1, Integer::sum); if (x || y != '\u0000') { throw new RuntimeException("Must be false and \u0000"); } @@ -686,7 +677,7 @@ public class TestBasics { @Test @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) public void twoArgsDefault6(char x, byte y) { - executed[49]++; + executed.merge("twoArgsDefault6", 1, Integer::sum); if (x != '\u0000' || y != 0) { throw new RuntimeException("Must be\u0000 and 0"); } @@ -695,7 +686,7 @@ public class TestBasics { @Test @Arguments(values = {Argument.RANDOM_ONCE, Argument.RANDOM_ONCE}) public void twoArgsRandomOnce(char x, byte y) { - executed[50]++; + executed.merge("twoArgsRandomOnce", 1, Integer::sum); } @Test @@ -707,7 +698,7 @@ public class TestBasics { if (Stream.of(a, b, c, d, e, f, g, h).allMatch(i -> i == a)) { throw new RuntimeException("RANDOM_ONCE does not produce random values for different arguments"); } - executed[51]++; + executed.merge("checkRandomOnceDifferentArgs", 1, Integer::sum); } @Test @@ -716,7 +707,7 @@ public class TestBasics { Argument.RANDOM_ONCE, Argument.RANDOM_ONCE, Argument.RANDOM_ONCE, Argument.RANDOM_ONCE}) public void checkMixedRandoms1(byte a, short b, int c, long d, char e, boolean f, float g, double h) { - executed[52]++; + executed.merge("checkMixedRandoms1", 1, Integer::sum); } @Test @@ -725,7 +716,7 @@ public class TestBasics { Argument.RANDOM_EACH, Argument.RANDOM_EACH, Argument.RANDOM_EACH, Argument.RANDOM_EACH}) public void checkMixedRandoms2(byte a, short b, int c, long d, char e, boolean f, float g, double h) { - executed[53]++; + executed.merge("checkMixedRandoms2", 1, Integer::sum); } @Test @@ -734,7 +725,7 @@ public class TestBasics { Argument.RANDOM_ONCE, Argument.RANDOM_EACH, Argument.RANDOM_EACH, Argument.RANDOM_ONCE}) public void checkMixedRandoms3(byte a, short b, int c, long d, char e, boolean f, float g, double h) { - executed[54]++; + executed.merge("checkMixedRandoms3", 1, Integer::sum); } @Test @@ -745,7 +736,7 @@ public class TestBasics { if (a != 42 || b != 42 || c != 42 || d != 42 || e != 42.0 || f != 42.0) { throw new RuntimeException("Must all be 42"); } - executed[55]++; + executed.merge("check42Mix1", 1, Integer::sum); } @Test @@ -756,7 +747,7 @@ public class TestBasics { if (a != -42 || b != -42 || c != -42 || d != -42 || e != -42.0 || f != -42.0) { throw new RuntimeException("Must all be -42"); } - executed[56]++; + executed.merge("check42Mix2", 1, Integer::sum); } @Test @@ -767,14 +758,14 @@ public class TestBasics { if (a != -42 || b != 42 || c != -42 || d != -42 || e != 42.0 || f != -42.0) { throw new RuntimeException("Do not match the right 42 version"); } - executed[57]++; + executed.merge("check42Mix3", 1, Integer::sum); } @Test @Arguments(values = Argument.BOOLEAN_TOGGLE_FIRST_TRUE) public void booleanToggleFirstTrue(boolean x) { - if (executed[58] == 0) { + if (executed.getOrDefault("booleanToggleFirstTrue", 0) == 0) { // First invocation if (!x) { throw new RuntimeException("BOOLEAN_TOGGLE_FIRST_FALSE must be false on first invocation"); @@ -783,13 +774,13 @@ public class TestBasics { throw new RuntimeException("BOOLEAN_TOGGLE_FIRST_FALSE did not toggle"); } lastToggleBoolean = x; - executed[58]++; + executed.merge("booleanToggleFirstTrue", 1, Integer::sum); } @Test @Arguments(values = {Argument.BOOLEAN_TOGGLE_FIRST_FALSE, Argument.BOOLEAN_TOGGLE_FIRST_TRUE}) public void checkTwoToggles(boolean b1, boolean b2) { - if (executed[59] == 0) { + if (executed.getOrDefault("checkTwoToggles", 0) == 0) { // First invocation if (b1 || !b2) { throw new RuntimeException("BOOLEAN_TOGGLES have wrong initial value"); @@ -800,14 +791,14 @@ public class TestBasics { throw new RuntimeException("Booleans did not toggle"); } lastToggleBoolean = b1; - executed[59]++; + executed.merge("checkTwoToggles", 1, Integer::sum); } @Test @Arguments(values = {Argument.BOOLEAN_TOGGLE_FIRST_FALSE, Argument.FALSE, Argument.TRUE, Argument.BOOLEAN_TOGGLE_FIRST_TRUE}) public void booleanMix(boolean b1, boolean b2, boolean b3, boolean b4) { - if (executed[60] == 0) { + if (executed.getOrDefault("booleanMix", 0) == 0) { // First invocation if (b1 || b2 || !b3 || !b4) { throw new RuntimeException("BOOLEAN_TOGGLES have wrong initial value"); @@ -818,7 +809,7 @@ public class TestBasics { throw new RuntimeException("Booleans did not toggle"); } lastToggleBoolean = b1; - executed[60]++; + executed.merge("booleanMix", 1, Integer::sum); } /* @@ -827,19 +818,19 @@ public class TestBasics { @Test public int testCheck() { - executed[63]++; + executed.merge("testCheck", 1, Integer::sum); return 1; } // Checked test. Check invoked after invoking "testCheck". Perform some more things after invocation. @Check(test = "testCheck") public void checkTestCheck() { - executed[64]++; // Executed on each invocation + executed.merge("checkTestCheck", 1, Integer::sum); // Executed on each invocation } @Test public int testCheckReturn() { - executed[65]++; + executed.merge("testCheckReturn", 1, Integer::sum); return 2; } @@ -849,13 +840,13 @@ public class TestBasics { if (returnValue != 2) { throw new RuntimeException("Must be 2"); } - executed[66]++; // Executed on each invocation + executed.merge("checkTestCheckReturn", 1, Integer::sum); // Executed on each invocation } @Test @Arguments(values = Argument.NUMBER_42) public short testCheckWithArgs(short x) { - executed[94]++; + executed.merge("testCheckWithArgs", 1, Integer::sum); return x; } @@ -864,25 +855,25 @@ public class TestBasics { if (returnValue != 42) { throw new RuntimeException("Must be 42"); } - executed[95]++; // Executed on each invocation + executed.merge("checkTestCheckWithArgs", 1, Integer::sum); // Executed on each invocation } @Test public int testCheckTestInfo() { - executed[67]++; + executed.merge("testCheckTestInfo", 1, Integer::sum); return 3; } // Checked test with info object about test. @Check(test = "testCheckTestInfo") public void checkTestCheckTestInfo(TestInfo testInfo) { - executed[68]++; // Executed on each invocation + executed.merge("checkTestCheckTestInfo(TestInfo)", 1, Integer::sum); // Executed on each invocation } @Test public int testCheckBoth() { - executed[69]++; + executed.merge("testCheckBoth", 1, Integer::sum); return 4; } @@ -892,12 +883,12 @@ public class TestBasics { if (returnValue != 4) { throw new RuntimeException("Must be 4"); } - executed[70]++; // Executed on each invocation + executed.merge("checkTestCheckTestInfo(int, TestInfo)", 1, Integer::sum); // Executed on each invocation } @Test public int testCheckOnce() { - executed[71]++; + executed.merge("testCheckOnce", 1, Integer::sum); return 1; } @@ -909,7 +900,7 @@ public class TestBasics { @Test public int testCheckReturnOnce() { - executed[72]++; + executed.merge("testCheckReturnOnce", 1, Integer::sum); return 2; } @@ -923,7 +914,7 @@ public class TestBasics { @Test public int testCheckTestInfoOnce() { - executed[73]++; + executed.merge("testCheckTestInfoOnce", 1, Integer::sum); return 3; } @@ -934,7 +925,7 @@ public class TestBasics { @Test public int testCheckBothOnce() { - executed[74]++; + executed.merge("testCheckBothOnce", 1, Integer::sum); return 4; } @@ -946,42 +937,9 @@ public class TestBasics { executedOnce[3]++; // Executed once } - @Test - public void sameName() { - executed[76]++; - } - - // Allowed to overload test method if not test method itself - public void sameName(boolean a) { - wasExecuted = true; - } - - // Allowed to overload test method if not test method itself - @Check(test = "sameName") - public void sameName(TestInfo info) { - executed[77]++; - } - - - /* - * Custom run tests. - */ - - @Test - public void sameName2() { - executed[92]++; - } - - // Allowed to overload test method if not test method itself - @Run(test = "sameName2") - public void sameName2(RunInfo info) { - executed[93]++; - sameName2(); - } - @Test public void testRun() { - executed[61]++; + executed.merge("testRun", 1, Integer::sum); } // Custom run test. This method is invoked each time instead of @Test method. This method responsible for calling @@ -993,7 +951,7 @@ public class TestBasics { @Test public void testRunNoTestInfo(int i) { // Argument allowed when run by @Run - executed[62]++; + executed.merge("testRunNoTestInfo", 1, Integer::sum); } @Run(test = "testRunNoTestInfo") @@ -1025,7 +983,7 @@ public class TestBasics { @Test public void testRunOnce2() { - executed[75]++; + executed.merge("testRunOnce2", 1, Integer::sum); } @Run(test = "testRunOnce2", mode = RunMode.STANDALONE) @@ -1037,12 +995,12 @@ public class TestBasics { @Test public void testRunMultiple() { - executed[96]++; + executed.merge("testRunMultiple", 1, Integer::sum); } @Test public void testRunMultiple2() { - executed[97]++; + executed.merge("testRunMultiple2", 1, Integer::sum); } @Test @@ -1059,12 +1017,12 @@ public class TestBasics { @Test public void testRunMultiple3() { - executed[98]++; + executed.merge("testRunMultiple3", 1, Integer::sum); } @Test public void testRunMultiple4() { - executed[99]++; + executed.merge("testRunMultiple4", 1, Integer::sum); } @Test diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestCheckedTests.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestCheckedTests.java index 5b7cd3c38a8..d4c12de3e4b 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestCheckedTests.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestCheckedTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -180,6 +180,12 @@ class BadIRAndRuntimeCheckedTests { throw new BadCheckedTestException("expected"); } } + + static class BadCheckedTestException extends RuntimeException { + BadCheckedTestException(String s) { + super(s); + } + } } class BadIRCheckedTests { @@ -224,9 +230,3 @@ class BadIRCheckedTests { } } } - -class BadCheckedTestException extends RuntimeException { - BadCheckedTestException(String s) { - super(s); - } -} diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestControls.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestControls.java index 7fa834b3e55..05b7007c9bd 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestControls.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestControls.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ import jdk.test.lib.Asserts; import jdk.test.whitebox.WhiteBox; import java.lang.reflect.Method; +import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -46,7 +47,7 @@ import java.util.regex.Pattern; */ public class TestControls { - static int[] executed = new int[15]; + static HashMap executed = HashMap.newHashMap(15); static boolean wasExecuted = false; static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); @@ -56,31 +57,21 @@ public class TestControls { Method runTestsOnSameVM = TestVM.class.getDeclaredMethod("runTestsOnSameVM", Class.class); runTestsOnSameVM.setAccessible(true); runTestsOnSameVM.invoke(null, new Object[]{ null }); - final int defaultIterations = TestVM.WARMUP_ITERATIONS + 1; - Asserts.assertEQ(executed[0], 1001); - Asserts.assertEQ(executed[1], 101); - Asserts.assertEQ(executed[2], 10000); - Asserts.assertEQ(executed[3], 10000); - Asserts.assertEQ(executed[4], defaultIterations); - Asserts.assertEQ(executed[5], defaultIterations); - Asserts.assertEQ(executed[6], 5001); - Asserts.assertEQ(executed[7], 5001); - Asserts.assertEQ(executed[8], 1); - Asserts.assertEQ(executed[9], 5000); - Asserts.assertEQ(executed[10], 1); - Asserts.assertEQ(executed[11], 2); - Asserts.assertEQ(executed[12], 1); - Asserts.assertEQ(executed[13], 1); + Asserts.assertEQ(executed.get("test1"), 1001); + Asserts.assertEQ(executed.get("test2"), 101); + Asserts.assertEQ(executed.get("testDontCompile"), 10000); + Asserts.assertEQ(executed.get("dontCompile"), 10000); + Asserts.assertEQ(executed.get("testCompileAtLevel1"), 5001); + Asserts.assertEQ(executed.get("dontCompile2"), 5001); + Asserts.assertEQ(executed.get("runTestDontCompile2A"), 1); + Asserts.assertEQ(executed.get("runTestDontCompile2B"), 5000); + Asserts.assertEQ(executed.get("noWarmup"), 1); + Asserts.assertEQ(executed.get("noWarmup2"), 2); + Asserts.assertEQ(executed.get("runNoWarmup2"), 1); + Asserts.assertEQ(executed.get("runTestCompilation"), 1); Asserts.assertFalse(wasExecuted); final long started = System.currentTimeMillis(); long elapsed = 0; - Method overloadDouble = TestControls.class.getDeclaredMethod("overload", double.class); - Method overloadInt = TestControls.class.getDeclaredMethod("overload", int.class); - while (!(TestFramework.isC2Compiled(overloadInt) && TestFramework.isCompiledAtLevel(overloadDouble, CompLevel.C1_LIMITED_PROFILE)) && elapsed < 5000) { - elapsed = System.currentTimeMillis() - started; - } - TestFramework.assertCompiledAtLevel(TestControls.class.getDeclaredMethod("overload", double.class), CompLevel.C1_LIMITED_PROFILE); - TestFramework.assertCompiledByC2(TestControls.class.getDeclaredMethod("overload", int.class)); TestFramework framework = new TestFramework(ClassInitializerTest.class); framework.addFlags("-XX:+PrintCompilation").addHelperClasses(ClassInitializerHelper.class).start(); @@ -99,15 +90,15 @@ public class TestControls { @Test @Warmup(1000) public void test1() { - executed[0]++; + executed.merge("test1", 1, Integer::sum); } @Check(test = "test1") public void check1(TestInfo info) { - if (executed[0] <= 1000) { + if (executed.getOrDefault("test1", 0) <= 1000) { Asserts.assertTrue(info.isWarmUp()); } else { - Asserts.assertTrue(!info.isWarmUp() && executed[0] == 1001); + Asserts.assertTrue(!info.isWarmUp() && executed.getOrDefault("test1", 0) == 1001); TestFramework.assertCompiledByC2(info.getTest()); } } @@ -115,45 +106,23 @@ public class TestControls { @Test @Warmup(100) public void test2() { - executed[1]++; + executed.merge("test2", 1, Integer::sum); } @Check(test = "test2", when = CheckAt.COMPILED) public void check2(TestInfo info) { - Asserts.assertTrue(!info.isWarmUp() && executed[1] == 101); + Asserts.assertTrue(!info.isWarmUp() && executed.getOrDefault("test2", 0) == 101); TestFramework.assertCompiledByC2(info.getTest()); } - @Test - public void overload() { - executed[4]++; - } - - @ForceCompile - @DontInline - public static void overload(int i) { - wasExecuted = true; - } - - @ForceCompile(CompLevel.C1_LIMITED_PROFILE) - @ForceInline - public static void overload(double i) { - wasExecuted = true; - } - - @Check(test = "overload") - public void checkOverload() { - executed[5]++; - } - @Test public void testDontCompile() { - executed[2]++; + executed.merge("testDontCompile", 1, Integer::sum); } @DontCompile public static void dontCompile() { - executed[3]++; + executed.merge("dontCompile", 1, Integer::sum); } @Run(test = "testDontCompile", mode = RunMode.STANDALONE) @@ -167,12 +136,12 @@ public class TestControls { @Test public void testCompileAtLevel1() { - executed[6]++; + executed.merge("testCompileAtLevel1", 1, Integer::sum); } @DontCompile(Compiler.ANY) public static void dontCompile2() { - executed[7]++; + executed.merge("dontCompile2", 1, Integer::sum); } @Run(test = "testCompileAtLevel1") @@ -181,23 +150,23 @@ public class TestControls { dontCompile2(); testCompileAtLevel1(); if (!info.isWarmUp()) { - executed[8]++; + executed.merge("runTestDontCompile2A", 1, Integer::sum); int compLevel = WHITE_BOX.getMethodCompilationLevel(TestControls.class.getDeclaredMethod("dontCompile2"), false); Asserts.assertLessThan(compLevel, CompLevel.C1_LIMITED_PROFILE.getValue()); } else { - executed[9]++; + executed.merge("runTestDontCompile2B", 1, Integer::sum); } } @Test @Warmup(0) public void noWarmup() { - executed[10]++; + executed.merge("noWarmup", 1, Integer::sum); } @Test public void noWarmup2() { - executed[11]++; + executed.merge("noWarmup2", 1, Integer::sum); } @Run(test = "noWarmup2") @@ -206,7 +175,7 @@ public class TestControls { noWarmup2(); noWarmup2(); Asserts.assertTrue(!info.isWarmUp()); - executed[12]++; + executed.merge("runNoWarmup2", 1, Integer::sum); } @Test @@ -301,7 +270,7 @@ public class TestControls { TestFramework.assertCompiledAtLevel(info.getTestClassMethod("forceC1DontC2"), CompLevel.C1_SIMPLE); TestFramework.assertCompiledAtLevel(info.getTestClassMethod("forceC2DontC1"), CompLevel.C2); - executed[13]++; + executed.merge("runTestCompilation", 1, Integer::sum); } } diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestSetupTests.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestSetupTests.java index 782a773515a..1cdb6c2797a 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestSetupTests.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestSetupTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -25,7 +25,6 @@ package ir_framework.tests; import compiler.lib.ir_framework.*; import compiler.lib.ir_framework.driver.TestVMException; -import compiler.lib.ir_framework.shared.TestRunException; import java.io.ByteArrayOutputStream; import java.io.PrintStream; @@ -360,10 +359,11 @@ class TestSetupTestsWithExpectedExceptions { public void checkThrowInCheck(int x) { throw new BadCheckedTestException("expected check"); } -} -class BadCheckedTestException extends RuntimeException { - BadCheckedTestException(String s) { - super(s); + + static class BadCheckedTestException extends RuntimeException { + BadCheckedTestException(String s) { + super(s); + } } } diff --git a/test/jaxp/ProblemList.txt b/test/jaxp/ProblemList.txt index 23ebb07f5ff..8f7380a43f8 100644 --- a/test/jaxp/ProblemList.txt +++ b/test/jaxp/ProblemList.txt @@ -1,6 +1,6 @@ ########################################################################### # -# Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2026, 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 @@ -23,3 +23,15 @@ # ########################################################################### +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-AotJdk.txt b/test/jdk/ProblemList-AotJdk.txt index 20960652dcf..c98bf63b25f 100644 --- a/test/jdk/ProblemList-AotJdk.txt +++ b/test/jdk/ProblemList-AotJdk.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2024, 2026, 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 @@ -44,3 +44,15 @@ java/lang/invoke/DumpMethodHandleInternals.java 0000000 generic- # this message will not be printed in the production run. java/util/Locale/UseOldISOCodesTest.java 0000000 generic-all +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-StaticJdk.txt b/test/jdk/ProblemList-StaticJdk.txt index 70f0438c0c6..700099656e8 100644 --- a/test/jdk/ProblemList-StaticJdk.txt +++ b/test/jdk/ProblemList-StaticJdk.txt @@ -1,6 +1,6 @@ ########################################################################### # -# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2025, 2026, 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 @@ -24,3 +24,16 @@ ########################################################################### # Currently empty + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-Virtual.txt b/test/jdk/ProblemList-Virtual.txt index fc5f31e3ce8..52f187086de 100644 --- a/test/jdk/ProblemList-Virtual.txt +++ b/test/jdk/ProblemList-Virtual.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2022, 2026, 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 @@ -34,3 +34,15 @@ java/lang/ScopedValue/StressStackOverflow.java#default 8309646 generic-all java/lang/ScopedValue/StressStackOverflow.java#no-TieredCompilation 8309646 generic-all java/lang/ScopedValue/StressStackOverflow.java#TieredStopAtLevel1 8309646 generic-all +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-Xcomp.txt b/test/jdk/ProblemList-Xcomp.txt index 5ed171a1fea..44bad411f26 100644 --- a/test/jdk/ProblemList-Xcomp.txt +++ b/test/jdk/ProblemList-Xcomp.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2026, 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,3 +29,16 @@ java/lang/invoke/MethodHandles/CatchExceptionTest.java 8146623 generic-all java/lang/reflect/callerCache/ReflectionCallerCacheTest.java 8332028 generic-all + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-coh.txt b/test/jdk/ProblemList-coh.txt new file mode 100644 index 00000000000..b3bddb0c9f4 --- /dev/null +++ b/test/jdk/ProblemList-coh.txt @@ -0,0 +1,41 @@ +# +# Copyright (c) 2026, 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. +# +# 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. +# + +############################################################################# +# +# List of quarantined tests for testing with -XX:+UseCompactObjectHeaders +# +############################################################################# + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-enable-preview.txt b/test/jdk/ProblemList-enable-preview.txt index 31e09d372a8..1a831a0dde3 100644 --- a/test/jdk/ProblemList-enable-preview.txt +++ b/test/jdk/ProblemList-enable-preview.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2025, 2026, 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 @@ -25,6 +25,8 @@ # # List of quarantined tests for testing with --enable-preview # +# These are failures that ONLY occur with the '--enable-preview' option +# specified. There are separate sub-sections for each preview project. +# ############################################################################# - diff --git a/test/jdk/ProblemList-jvmti-stress-agent.txt b/test/jdk/ProblemList-jvmti-stress-agent.txt index 09be19c3d94..e9019b6014e 100644 --- a/test/jdk/ProblemList-jvmti-stress-agent.txt +++ b/test/jdk/ProblemList-jvmti-stress-agent.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2025, 2026, 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,3 +29,16 @@ com/sun/jdi/ThreadMemoryLeakTest.java 0000 # weak referenced are not cleared java/lang/WeakPairMap/Driver.java 0000000 generic-all java/lang/ref/ReachabilityFenceTest.java 0000000 generic-all + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-shenandoah.txt b/test/jdk/ProblemList-shenandoah.txt index 063795d69e8..522b77cb502 100644 --- a/test/jdk/ProblemList-shenandoah.txt +++ b/test/jdk/ProblemList-shenandoah.txt @@ -58,3 +58,15 @@ jdk/jfr/jcmd/TestJcmdStartPathToGCRoots.java 8342951 generic-all jdk/jfr/jvm/TestWaste.java 8342951 generic-all jdk/jfr/startupargs/TestOldObjectQueueSize.java 8342951 generic-all +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-zgc.txt b/test/jdk/ProblemList-zgc.txt index ab44c5e47a9..065a52f9a83 100644 --- a/test/jdk/ProblemList-zgc.txt +++ b/test/jdk/ProblemList-zgc.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2026, 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 @@ -38,3 +38,16 @@ sun/tools/jhsdb/heapconfig/JMapHeapConfigTest.java 8307393 generic-all sun/tools/jhsdb/HeapDumpTestWithActiveProcess.java 8307393 generic-all com/sun/jdi/ThreadMemoryLeakTest.java 8307402 generic-all + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 291b2163b0d..36356c3c544 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -503,6 +503,7 @@ java/lang/invoke/LFCaching/LFMultiThreadCachingTest.java 8151492 generic- java/lang/invoke/LFCaching/LFGarbageCollectedTest.java 8078602 generic-all java/lang/invoke/lambda/LambdaFileEncodingSerialization.java 8249079 linux-all java/lang/invoke/RicochetTest.java 8251969 generic-all +java/lang/ProcessBuilder/PipelineLeaksFD.java 8375585 generic-all ############################################################################ @@ -777,3 +778,16 @@ java/awt/Cursor/CursorDragTest/ListDragCursor.java 7177297 macosx-all # jdk_since_checks tools/sincechecker/modules/jdk.management.jfr/JdkManagementJfrCheckSince.java 8354921 generic-all + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/build/CheckFiles.java b/test/jdk/build/CheckFiles.java index 5a915e881f0..7ef5b803d2d 100644 --- a/test/jdk/build/CheckFiles.java +++ b/test/jdk/build/CheckFiles.java @@ -106,6 +106,8 @@ public class CheckFiles { allowedEndingsLibDir.add("psfont.properties.ja"); allowedEndingsLibDir.add("src.zip"); allowedEndingsLibDir.add("tzdb.dat"); + allowedEndingsLibDir.add("javafx-swt.jar"); + allowedEndingsLibDir.add("javafx.properties"); if (Platform.isWindows()) { allowedEndingsLibDir.add(".lib"); allowedEndingsLibDir.add("tzmappings"); diff --git a/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java b/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java index b47433ca16f..b139f12da25 100644 --- a/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java +++ b/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -131,6 +131,7 @@ public class LdapPoolTimeoutTest { || msg.contains("No route to host") || msg.contains("Timed out waiting for lock") || msg.contains("Connect timed out") + || msg.contains("Connection timed out") || msg.contains("Timeout exceeded while waiting for a connection"))) { // got the expected exception System.out.println("Received expected NamingException with message: " + msg); diff --git a/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.java b/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.java index d89de44401b..13a88e8c590 100644 --- a/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.java +++ b/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8255800 8369129 + * @bug 8255800 8369129 8376297 * @summary verify Raster + SampleModel creation vs spec. */ @@ -739,7 +739,7 @@ public class CreateRasterExceptionTest { /* @throws IllegalArgumentException if * {@code scanlineStride} is less than 0 */ - Raster.createBandedRaster(DataBuffer.TYPE_INT, 1, 1, -3, + Raster.createBandedRaster(DataBuffer.TYPE_INT, 10, 10, -1000, bankIndices, bandOffsets, null); noException(); } catch (IllegalArgumentException t) { diff --git a/test/jdk/java/awt/print/PrinterJob/PageRanges.java b/test/jdk/java/awt/print/PrinterJob/PageRanges.java index aea60516f78..691357d5a3c 100644 --- a/test/jdk/java/awt/print/PrinterJob/PageRanges.java +++ b/test/jdk/java/awt/print/PrinterJob/PageRanges.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6575331 8297191 + * @bug 6575331 8297191 8373239 * @key printer * @summary The specified pages should be printed. * @library /java/awt/regtesthelpers diff --git a/test/jdk/java/lang/reflect/AccessibleObject/CanAccessTest.java b/test/jdk/java/lang/reflect/AccessibleObject/CanAccessTest.java index 45e2502b601..ac7a1bad31b 100644 --- a/test/jdk/java/lang/reflect/AccessibleObject/CanAccessTest.java +++ b/test/jdk/java/lang/reflect/AccessibleObject/CanAccessTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -21,11 +21,11 @@ * questions. */ -/** +/* * @test * @build CanAccessTest * @modules java.base/jdk.internal.misc:+open - * @run testng/othervm CanAccessTest + * @run junit/othervm CanAccessTest * @summary Test AccessibleObject::canAccess method */ @@ -34,31 +34,29 @@ import java.lang.reflect.Method; import java.security.SecureClassLoader; import jdk.internal.misc.Unsafe; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; -@Test public class CanAccessTest { private static Unsafe INSTANCE = Unsafe.getUnsafe(); /** * null object parameter for Constructor */ + @Test public void testConstructor() throws Exception { Constructor ctor = Unsafe.class.getDeclaredConstructor(); assertFalse(ctor.canAccess(null)); assertTrue(ctor.trySetAccessible()); - try { - // non-null object parameter - ctor.canAccess(INSTANCE); - assertTrue(false); - } catch (IllegalArgumentException expected) {} + // non-null object parameter + assertThrows(IllegalArgumentException.class, () -> ctor.canAccess(INSTANCE)); } /** * Test protected constructors */ + @Test public void testProtectedConstructor() throws Exception { TestLoader.testProtectedConstructorNonOpenedPackage(); @@ -69,21 +67,20 @@ public class CanAccessTest { /** * null object parameter for static members */ + @Test public void testStaticMember() throws Exception { Method m = Unsafe.class.getDeclaredMethod("throwIllegalAccessError"); assertFalse(m.canAccess(null)); assertTrue(m.trySetAccessible()); - try { - // non-null object parameter - m.canAccess(INSTANCE); - assertTrue(false); - } catch (IllegalArgumentException expected) { } + // non-null object parameter + assertThrows(IllegalArgumentException.class, () -> m.canAccess(INSTANCE)); } /** * Test protected static */ + @Test public void testProtectedStatic() throws Exception { Method m = TestLoader.testProtectedStatic(); assertFalse(m.canAccess(null)); @@ -93,28 +90,24 @@ public class CanAccessTest { * the specified object must be an instance of the declaring class * for instance members */ + @Test public void testInstanceMethod() throws Exception { Method m = Unsafe.class.getDeclaredMethod("allocateMemory0", long.class); assertFalse(m.canAccess(INSTANCE)); - try { - m.canAccess(null); - assertTrue(false); - } catch (IllegalArgumentException expected) { } + assertThrows(IllegalArgumentException.class, () -> m.canAccess(null)); } /** * the specified object must be an instance of the declaring class * for instance members */ + @Test public void testInvalidInstanceObject() throws Exception { Class clazz = Class.forName("sun.security.x509.X500Name"); Method m = clazz.getDeclaredMethod("size"); - try { - m.canAccess(INSTANCE); - assertTrue(false); - } catch (IllegalArgumentException expected) { } + assertThrows(IllegalArgumentException.class, () -> m.canAccess(INSTANCE)); } diff --git a/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java b/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java index 45cebdb552b..7d79a1ac49b 100644 --- a/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java +++ b/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -21,12 +21,12 @@ * questions. */ -/** +/* * @test * @build ModuleSetAccessibleTest * @modules java.base/java.lang:open * java.base/jdk.internal.misc:+open - * @run testng/othervm ModuleSetAccessibleTest + * @run junit/othervm ModuleSetAccessibleTest * @summary Test java.lang.reflect.AccessibleObject with modules */ @@ -40,22 +40,19 @@ import java.security.ProtectionDomain; import jdk.internal.misc.Unsafe; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; -@Test public class ModuleSetAccessibleTest { /** * Invoke a private constructor on a public class in an exported package */ + @Test public void testPrivateConstructorInExportedPackage() throws Exception { Constructor ctor = Unsafe.class.getDeclaredConstructor(); - try { - ctor.newInstance(); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> ctor.newInstance()); ctor.setAccessible(true); Unsafe unsafe = (Unsafe) ctor.newInstance(); @@ -65,34 +62,26 @@ public class ModuleSetAccessibleTest { /** * Invoke a private method on a public class in an exported package */ + @Test public void testPrivateMethodInExportedPackage() throws Exception { Method m = Unsafe.class.getDeclaredMethod("throwIllegalAccessError"); - try { - m.invoke(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> m.invoke(null)); m.setAccessible(true); - try { - m.invoke(null); - assertTrue(false); - } catch (InvocationTargetException e) { - // thrown by throwIllegalAccessError - assertTrue(e.getCause() instanceof IllegalAccessError); - } + InvocationTargetException e = assertThrows(InvocationTargetException.class, () -> + m.invoke(null)); + // thrown by throwIllegalAccessError + assertInstanceOf(IllegalAccessError.class, e.getCause()); } /** * Access a private field in a public class that is an exported package */ + @Test public void testPrivateFieldInExportedPackage() throws Exception { Field f = Unsafe.class.getDeclaredField("theUnsafe"); - - try { - f.get(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> f.get(null)); f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null); @@ -102,19 +91,14 @@ public class ModuleSetAccessibleTest { /** * Invoke a public constructor on a public class in a non-exported package */ + @Test public void testPublicConstructorInNonExportedPackage() throws Exception { Class clazz = Class.forName("sun.security.x509.X500Name"); Constructor ctor = clazz.getConstructor(String.class); - try { - ctor.newInstance("cn=duke"); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> ctor.newInstance("cn=duke")); - try { - ctor.setAccessible(true); - assertTrue(false); - } catch (InaccessibleObjectException expected) { } + assertThrows(InaccessibleObjectException.class, () -> ctor.setAccessible(true)); ctor.setAccessible(false); // should succeed } @@ -123,19 +107,14 @@ public class ModuleSetAccessibleTest { /** * Access a public field in a public class that in a non-exported package */ + @Test public void testPublicFieldInNonExportedPackage() throws Exception { Class clazz = Class.forName("sun.security.x509.X500Name"); Field f = clazz.getField("SERIALNUMBER_OID"); - try { - f.get(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> f.get(null)); - try { - f.setAccessible(true); - assertTrue(false); - } catch (InaccessibleObjectException expected) { } + assertThrows(InaccessibleObjectException.class, () -> f.setAccessible(true)); f.setAccessible(false); // should succeed } @@ -144,6 +123,7 @@ public class ModuleSetAccessibleTest { /** * Test that the Class constructor cannot be make accessible. */ + @Test public void testJavaLangClass() throws Exception { // non-public constructor @@ -152,15 +132,8 @@ public class ModuleSetAccessibleTest { ProtectionDomain.class, boolean.class, char.class); AccessibleObject[] ctors = { ctor }; - try { - ctor.setAccessible(true); - assertTrue(false); - } catch (SecurityException expected) { } - - try { - AccessibleObject.setAccessible(ctors, true); - assertTrue(false); - } catch (SecurityException expected) { } + assertThrows(SecurityException.class, () -> ctor.setAccessible(true)); + assertThrows(SecurityException.class, () -> AccessibleObject.setAccessible(ctors, true)); // should succeed ctor.setAccessible(false); diff --git a/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java b/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java index 774ee4d1dad..bd4d0fab23f 100644 --- a/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java +++ b/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -21,14 +21,14 @@ * questions. */ -/** +/* * @test * @build TrySetAccessibleTest * @modules java.base/java.lang:open * java.base/jdk.internal.module * java.base/jdk.internal.perf * java.base/jdk.internal.misc:+open - * @run testng/othervm TrySetAccessibleTest + * @run junit/othervm TrySetAccessibleTest * @summary Test AccessibleObject::trySetAccessible method */ @@ -43,21 +43,18 @@ import jdk.internal.module.ModulePath; import jdk.internal.perf.Perf; import java.security.ProtectionDomain; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; -@Test public class TrySetAccessibleTest { /** * Invoke a private constructor on a public class in an exported package */ + @Test public void testPrivateConstructorInExportedPackage() throws Exception { Constructor ctor = Perf.class.getDeclaredConstructor(); - try { - ctor.newInstance(); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> ctor.newInstance()); assertFalse(ctor.trySetAccessible()); assertFalse(ctor.canAccess(null)); @@ -66,13 +63,11 @@ public class TrySetAccessibleTest { /** * Invoke a private constructor on a public class in an open package */ + @Test public void testPrivateConstructorInOpenedPackage() throws Exception { Constructor ctor = Unsafe.class.getDeclaredConstructor(); - try { - ctor.newInstance(); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> ctor.newInstance()); assertTrue(ctor.trySetAccessible()); assertTrue(ctor.canAccess(null)); @@ -82,12 +77,10 @@ public class TrySetAccessibleTest { /** * Invoke a private method on a public class in an exported package */ + @Test public void testPrivateMethodInExportedPackage() throws Exception { Method m = ModulePath.class.getDeclaredMethod("packageName", String.class); - try { - m.invoke(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> m.invoke(null)); assertFalse(m.trySetAccessible()); assertFalse(m.canAccess(null)); @@ -97,54 +90,42 @@ public class TrySetAccessibleTest { /** * Invoke a private method on a public class in an open package */ + @Test public void testPrivateMethodInOpenedPackage() throws Exception { Method m = Unsafe.class.getDeclaredMethod("throwIllegalAccessError"); assertFalse(m.canAccess(null)); - try { - m.invoke(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> m.invoke(null)); assertTrue(m.trySetAccessible()); assertTrue(m.canAccess(null)); - try { - m.invoke(null); - assertTrue(false); - } catch (InvocationTargetException e) { - // thrown by throwIllegalAccessError - assertTrue(e.getCause() instanceof IllegalAccessError); - } + + InvocationTargetException e = assertThrows(InvocationTargetException.class, () -> + m.invoke(null)); + assertInstanceOf(IllegalAccessError.class, e.getCause()); } /** * Invoke a private method on a public class in an exported package */ + @Test public void testPrivateFieldInExportedPackage() throws Exception { Field f = Perf.class.getDeclaredField("instance"); - try { - f.get(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> f.get(null)); assertFalse(f.trySetAccessible()); assertFalse(f.canAccess(null)); - try { - f.get(null); - assertTrue(false); - } catch (IllegalAccessException expected) {} + assertThrows(IllegalAccessException.class, () -> f.get(null)); } /** * Access a private field in a public class that is an exported package */ + @Test public void testPrivateFieldInOpenedPackage() throws Exception { Field f = Unsafe.class.getDeclaredField("theUnsafe"); - try { - f.get(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> f.get(null)); assertTrue(f.trySetAccessible()); assertTrue(f.canAccess(null)); @@ -155,32 +136,29 @@ public class TrySetAccessibleTest { /** * Invoke a public constructor on a public class in a non-exported package */ + @Test public void testPublicConstructorInNonExportedPackage() throws Exception { Class clazz = Class.forName("sun.security.x509.X500Name"); Constructor ctor = clazz.getConstructor(String.class); - try { - ctor.newInstance("cn=duke"); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> ctor.newInstance("cn=duke")); assertFalse(ctor.trySetAccessible()); assertFalse(ctor.canAccess(null)); - assertTrue(ctor.trySetAccessible() == ctor.isAccessible()); + assertFalse(ctor.trySetAccessible()); + assertFalse(ctor.isAccessible()); // should match trySetAccessible } /** * Access a public field in a public class that in a non-exported package */ + @Test public void testPublicFieldInNonExportedPackage() throws Exception { Class clazz = Class.forName("sun.security.x509.X500Name"); Field f = clazz.getField("SERIALNUMBER_OID"); - try { - f.get(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> f.get(null)); assertFalse(f.trySetAccessible()); assertFalse(f.canAccess(null)); @@ -190,6 +168,7 @@ public class TrySetAccessibleTest { /** * Test that the Class constructor cannot be make accessible. */ + @Test public void testJavaLangClass() throws Exception { // non-public constructor diff --git a/test/jdk/java/lang/reflect/ChainedReflection.java b/test/jdk/java/lang/reflect/ChainedReflection.java index 212a70345cd..bd76cd13fc8 100644 --- a/test/jdk/java/lang/reflect/ChainedReflection.java +++ b/test/jdk/java/lang/reflect/ChainedReflection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -23,9 +23,11 @@ /* * @test - * @run testng/othervm ChainedReflection + * @run junit/othervm ChainedReflection * @summary Test Method::invoke and Constructor::newInstance chained calls that * should wrap NPE in InvocationTargetException properly + * @comment This test is not using assertThrows given lambdas may affect the + * exception stack trace and therefore test anticipations */ import java.lang.reflect.Constructor; @@ -33,24 +35,28 @@ import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.util.Optional; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; public class ChainedReflection { - public ChainedReflection() {} - ChainedReflection(Void dummy) throws ReflectiveOperationException { - Method m = ChainedReflection.class.getMethod("throwNPE"); - try { - m.invoke(null); - } catch (InvocationTargetException e) { - Throwable t = e.getTargetException(); - if (t instanceof NullPointerException npe) { - throw npe; - } else { - throw new RuntimeException("Test failed (InvocationTargetException didn't wrap NullPointerException)"); + // This inner class is declared with a constructor that ctorCallMethodInvoke + // reflects on. Such a constructor cannot be declared in ChainedReflection + // because JUnit does not allow a test class to declare multiple constructors. + class Inner { + Inner() throws ReflectiveOperationException { + Method m = ChainedReflection.class.getMethod("throwNPE"); + try { + m.invoke(null); + } catch (InvocationTargetException e) { + Throwable t = e.getTargetException(); + if (t instanceof NullPointerException npe) { + throw npe; + } else { + throw new RuntimeException("Test failed (InvocationTargetException didn't wrap NullPointerException)"); + } + } catch (Throwable t) { + throw new RuntimeException("Test failed (Unexpected exception)", t); } - } catch (Throwable t) { - throw new RuntimeException("Test failed (Unexpected exception)", t); } } @@ -105,9 +111,9 @@ public class ChainedReflection { */ @Test public void ctorCallMethodInvoke() throws ReflectiveOperationException { - Constructor ctor = ChainedReflection.class.getDeclaredConstructor(Void.class); + Constructor ctor = ChainedReflection.Inner.class.getDeclaredConstructor(ChainedReflection.class); try { - ctor.newInstance((Void)null); + ctor.newInstance(this); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (!(t instanceof NullPointerException)) { diff --git a/test/jdk/java/lang/reflect/DefaultMethodMembers/FilterNotMostSpecific.java b/test/jdk/java/lang/reflect/DefaultMethodMembers/FilterNotMostSpecific.java index 1992f31d5de..b8738a383b0 100644 --- a/test/jdk/java/lang/reflect/DefaultMethodMembers/FilterNotMostSpecific.java +++ b/test/jdk/java/lang/reflect/DefaultMethodMembers/FilterNotMostSpecific.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ * @bug 8029674 * @summary Verify that the right interface methods are returned by * Class.getMethod() and Class.getMethods() - * @run testng FilterNotMostSpecific + * @run junit FilterNotMostSpecific */ import java.lang.reflect.*; @@ -39,14 +39,14 @@ import java.util.HashMap; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class FilterNotMostSpecific { - @Test(dataProvider="getCases") + @ParameterizedTest + @MethodSource("getCases") public void testGetMethod(Class iface) { boolean match = false; MethodDesc[] expectedMethods = iface.getAnnotationsByType(MethodDesc.class); @@ -67,7 +67,8 @@ public class FilterNotMostSpecific { assert(match); } - @Test(dataProvider="getCases") + @ParameterizedTest + @MethodSource("getCases") public void testGetMethods(Class iface) { List foundMethods = filterObjectMethods(iface.getMethods()); MethodDesc[] expectedMethods = iface.getAnnotationsByType(MethodDesc.class); @@ -84,7 +85,7 @@ public class FilterNotMostSpecific { fail("On: "+ iface +"\nDid not find " + toMethodString(expected) + " among " + foundMethods); } - assertEquals(foundMethods.size(), expectedMethods.length, + assertEquals(expectedMethods.length, foundMethods.size(), "\non: " + iface + "\nexpected: " + toMethodStrings(expectedMethods) + "\nfound: " + foundMethods + "\n"); @@ -565,8 +566,7 @@ public class FilterNotMostSpecific { @MethodDesc(name="m", declaringClass=F.class, isGetMethodReturn=true) abstract class H extends G implements F {} - @DataProvider - public Object[][] getCases() { return CASES; } + public static Object[][] getCases() { return CASES; } public static final Class[][] CASES = { { K1.class }, { K1M.class }, diff --git a/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticInvokeTest.java b/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticInvokeTest.java index 215ee3dd66a..ff7d12502b3 100644 --- a/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticInvokeTest.java +++ b/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticInvokeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, 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 @@ -27,7 +27,7 @@ * in interfaces and/or in inheritance * @bug 7184826 * @build helper.Mod helper.Declared DefaultStaticTestData - * @run testng DefaultStaticInvokeTest + * @run junit DefaultStaticInvokeTest * @author Yong Lu */ @@ -40,23 +40,19 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.fail; -import org.testng.annotations.Test; - import static helper.Mod.*; import static helper.Declared.*; -import helper.Mod; +import static org.junit.jupiter.api.Assertions.*; +import helper.Mod; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class DefaultStaticInvokeTest { // getMethods(): Make sure getMethods returns the expected methods. - @Test(dataProvider = "testCasesAll", - dataProviderClass = DefaultStaticTestData.class) + @ParameterizedTest + @MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"}) public void testGetMethods(String testTarget, Object param) throws Exception { testMethods(ALL_METHODS, testTarget, param); @@ -64,8 +60,8 @@ public class DefaultStaticInvokeTest { // getDeclaredMethods(): Make sure getDeclaredMethods returns the expected methods. - @Test(dataProvider = "testCasesAll", - dataProviderClass = DefaultStaticTestData.class) + @ParameterizedTest + @MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"}) public void testGetDeclaredMethods(String testTarget, Object param) throws Exception { testMethods(DECLARED_ONLY, testTarget, param); @@ -73,8 +69,8 @@ public class DefaultStaticInvokeTest { // getMethod(): Make sure that getMethod finds all methods it should find. - @Test(dataProvider = "testCasesAll", - dataProviderClass = DefaultStaticTestData.class) + @ParameterizedTest + @MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"}) public void testGetMethod(String testTarget, Object param) throws Exception { @@ -91,8 +87,8 @@ public class DefaultStaticInvokeTest { // getMethod(): Make sure that getMethod does *not* find certain methods. - @Test(dataProvider = "testCasesAll", - dataProviderClass = DefaultStaticTestData.class) + @ParameterizedTest + @MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"}) public void testGetMethodSuperInterfaces(String testTarget, Object param) throws Exception { @@ -124,8 +120,8 @@ public class DefaultStaticInvokeTest { // Method.invoke(): Make sure Method.invoke returns the expected value. - @Test(dataProvider = "testCasesAll", - dataProviderClass = DefaultStaticTestData.class) + @ParameterizedTest + @MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"}) public void testMethodInvoke(String testTarget, Object param) throws Exception { Class typeUnderTest = Class.forName(testTarget); @@ -141,8 +137,8 @@ public class DefaultStaticInvokeTest { // MethodHandle.invoke(): Make sure MethodHandle.invoke returns the expected value. - @Test(dataProvider = "testCasesAll", - dataProviderClass = DefaultStaticTestData.class) + @ParameterizedTest + @MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"}) public void testMethodHandleInvoke(String testTarget, Object param) throws Throwable { Class typeUnderTest = Class.forName(testTarget); @@ -169,14 +165,14 @@ public class DefaultStaticInvokeTest { : (String) methodHandle.invoke(typeUnderTest.newInstance(), param); } - assertEquals(result, expectedReturn); + assertEquals(expectedReturn, result); } } // Lookup.findStatic / .findVirtual: Make sure IllegalAccessException is thrown as expected. - @Test(dataProvider = "testClasses", - dataProviderClass = DefaultStaticTestData.class) + @ParameterizedTest + @MethodSource("DefaultStaticTestData#testClasses") public void testIAE(String testTarget, Object param) throws ClassNotFoundException { @@ -189,14 +185,8 @@ public class DefaultStaticInvokeTest { if (mod != STATIC && typeUnderTest.isInterface()) { continue; } - Exception caught = null; - try { - getTestMH(typeUnderTest, mName, param, true); - } catch (Exception e) { - caught = e; - } - assertNotNull(caught); - assertEquals(caught.getClass(), IllegalAccessException.class); + assertThrowsExactly(IllegalAccessException.class, () -> + getTestMH(typeUnderTest, mName, param, true)); } } @@ -254,7 +244,7 @@ public class DefaultStaticInvokeTest { } } - assertEquals(myMethods.size(), expectedMethods.length); + assertEquals(expectedMethods.length, myMethods.size()); for (MethodDesc toTest : expectedMethods) { @@ -284,16 +274,15 @@ public class DefaultStaticInvokeTest { assertFalse(method.isDefault()); // Test invoke it - assertEquals(tryInvoke(method, null, param), expectedReturn); + assertEquals(expectedReturn, tryInvoke(method, null, param)); break; case DEFAULT: // if typeUnderTest is a class then instantiate and invoke if (!typeUnderTest.isInterface()) { - assertEquals(tryInvoke( + assertEquals(expectedReturn, tryInvoke( method, typeUnderTest, - param), - expectedReturn); + param)); } //assert candidate is default @@ -302,11 +291,10 @@ public class DefaultStaticInvokeTest { break; case REGULAR: // if typeUnderTest must be a class - assertEquals(tryInvoke( + assertEquals(expectedReturn, tryInvoke( method, typeUnderTest, - param), - expectedReturn); + param)); //assert candidate is neither default nor static assertFalse(Modifier.isStatic(method.getModifiers())); diff --git a/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticTestData.java b/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticTestData.java index 8049712569b..add991e3493 100644 --- a/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticTestData.java +++ b/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticTestData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, 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 @@ -27,12 +27,10 @@ * @author Yong Lu */ +import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.testng.annotations.DataProvider; -import org.testng.collections.Lists; - import static helper.Mod.*; import static helper.Declared.*; import helper.Mod; @@ -379,7 +377,6 @@ public class DefaultStaticTestData { * data is the name of the class under test Second data used in test as the * arguments used for the method call. */ - @DataProvider static Object[][] testClasses() { return new Object[][]{ {"TestClass1", null}, @@ -408,7 +405,6 @@ public class DefaultStaticTestData { * data is the name of the interface under test Second data used in test as * the arguments used for the method call. */ - @DataProvider static Object[][] testInterfaces() { return new Object[][]{ {"TestIF1", null}, @@ -438,12 +434,4 @@ public class DefaultStaticTestData { {"TestIF21", null}, }; } - - @DataProvider - static Object[][] testCasesAll() { - List result = Lists.newArrayList(); - result.addAll(Arrays.asList(testClasses())); - result.addAll(Arrays.asList(testInterfaces())); - return result.toArray(new Object[result.size()][]); - } } diff --git a/test/jdk/java/lang/reflect/Field/NegativeTest.java b/test/jdk/java/lang/reflect/Field/NegativeTest.java index fe1f2dd7480..186267f4458 100644 --- a/test/jdk/java/lang/reflect/Field/NegativeTest.java +++ b/test/jdk/java/lang/reflect/Field/NegativeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -21,20 +21,20 @@ * questions. */ -/** +/* * @test * @bug 8277451 * @summary Test exception thrown due to bad receiver and bad value on * Field with and without setAccessible(true) - * @run testng/othervm --enable-final-field-mutation=ALL-UNNAMED NegativeTest + * @run junit/othervm --enable-final-field-mutation=ALL-UNNAMED NegativeTest */ import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class NegativeTest { static class Fields { @@ -167,8 +167,7 @@ public class NegativeTest { } } - @DataProvider(name = "instanceFields") - private Object[][] instanceFields() { + private static Object[][] instanceFields() { return new Object[][]{ new Object[]{i_field}, new Object[]{c_field}, @@ -217,7 +216,8 @@ public class NegativeTest { * IllegalArgumentException is thrown if the receiver is of * a bad type. NullPointerException is thrown if the receiver is null. */ - @Test(dataProvider = "instanceFields") + @ParameterizedTest + @MethodSource("instanceFields") public void testReceiver(Field f) throws ReflectiveOperationException { f.get(INSTANCE); // good receiver @@ -231,15 +231,10 @@ public class NegativeTest { private void testBadReceiver(Field f) throws ReflectiveOperationException { assertFalse(Modifier.isStatic(f.getModifiers())); // instance field Object badObj = new NegativeTest(); - try { - f.get(badObj); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // expected - } + assertThrows(IllegalArgumentException.class, () -> f.get(badObj)); Class fType = f.getType(); if (fType.isPrimitive()) { - try { + assertThrows(IllegalArgumentException.class, () -> { switch (fType.descriptorString()) { case "B" -> f.getByte(badObj); case "C" -> f.getChar(badObj); @@ -250,10 +245,7 @@ public class NegativeTest { case "S" -> f.getShort(badObj); case "Z" -> f.getBoolean(badObj); } - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // expected - } + }); } } @@ -262,16 +254,11 @@ public class NegativeTest { */ private void testNullReceiver(Field f) throws ReflectiveOperationException { assertFalse(Modifier.isStatic(f.getModifiers())); // instance field - try { - f.get(null); - fail("expected NullPointerException"); - } catch (NullPointerException e) { - // expected - } + assertThrows(NullPointerException.class, () -> f.get(null)); Class fType = f.getType(); if (fType.isPrimitive()) { - try { + assertThrows(NullPointerException.class, () -> { switch (fType.descriptorString()) { case "B" -> f.getByte(null); case "C" -> f.getChar(null); @@ -282,15 +269,11 @@ public class NegativeTest { case "S" -> f.getShort(null); case "Z" -> f.getBoolean(null); } - fail("expected NullPointerException"); - } catch (NullPointerException e) { - // expected - } + }); } } - @DataProvider(name = "writeableFields") - private Object[][] writeableFields() { + private static Object[][] writeableFields() { Fields obj = new Fields(); return new Object[][]{ // instance fields with and without setAccessible(true) @@ -352,7 +335,8 @@ public class NegativeTest { * NullPointerException is thrown if the receiver of an instance field is null. * The receiver is checked */ - @Test(dataProvider = "writeableFields") + @ParameterizedTest + @MethodSource("writeableFields") public void testSetValue(Field f, Object obj, Object value) throws IllegalAccessException { f.set(obj, value); Class fType = f.getType(); @@ -369,25 +353,14 @@ public class NegativeTest { } // test null value only if it's primitive type - try { - f.set(obj, null); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // expected - } + assertThrows(IllegalArgumentException.class, () -> f.set(obj, null)); } Object badValue = new NegativeTest(); - try { - f.set(obj, badValue); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // expected - } + assertThrows(IllegalArgumentException.class, () -> f.set(obj, badValue)); } - @DataProvider(name = "readOnlyFinalFields") - private Object[][] readOnlyFinalFields() { + private static Object[][] readOnlyFinalFields() { Object obj = INSTANCE; return new Object[][]{ // instance final fields @@ -427,19 +400,15 @@ public class NegativeTest { * IllegalAccessException is thrown regardless of whether the value * is of a bad type or not. */ - @Test(dataProvider = "readOnlyFinalFields") + @ParameterizedTest + @MethodSource("readOnlyFinalFields") public void testSetValueOnFinalField(Field f, Object obj, Object value) { assertTrue(Modifier.isFinal(f.getModifiers())); - try { - f.set(obj, value); - fail("expected IllegalAccessException"); - } catch (IllegalAccessException e) { - // expected - } + assertThrows(IllegalAccessException.class, () -> f.set(obj, value)); Class fType = f.getType(); if (fType.isPrimitive()) { - try { + assertThrows(IllegalAccessException.class, () -> { switch (fType.descriptorString()) { case "B" -> f.setByte(obj, ((Byte)value).byteValue()); case "C" -> f.setChar(obj, ((Character)value).charValue()); @@ -450,33 +419,17 @@ public class NegativeTest { case "S" -> f.setShort(obj, ((Short)value).shortValue()); case "Z" -> f.setBoolean(obj, ((Boolean)value).booleanValue()); } - fail("expected IllegalAccessException"); - } catch (IllegalAccessException e) { - // expected - } + }); // test null value only if it's primitive type - try { - f.set(obj, null); - fail("expected IllegalAccessException"); - } catch (IllegalAccessException e) { - // expected - } + assertThrows(IllegalAccessException.class, () -> f.set(obj, null)); } Object badValue = new NegativeTest(); - try { - f.set(obj, badValue); - fail("expected IllegalAccessException"); - } catch (IllegalAccessException e) { - // expected - } + assertThrows(IllegalAccessException.class, () -> f.set(obj, badValue)); } - - - @DataProvider(name = "finalInstanceFields") - private Object[][] finalInstanceFields() { + private static Object[][] finalInstanceFields() { return new Object[][]{ new Object[]{fi_field, Integer.valueOf(10)}, new Object[]{fc_field, Character.valueOf('c')}, @@ -497,54 +450,27 @@ public class NegativeTest { * The receiver is checked before the access check is performed and * also before the value is checked. */ - @Test(dataProvider = "finalInstanceFields") + @ParameterizedTest + @MethodSource("finalInstanceFields") public void testReceiverOnFinalField(Field f, Object value) { assertTrue(Modifier.isFinal(f.getModifiers())); Object badReceiver = new NegativeTest(); // set the field with a bad receiver with a good value - try { - f.set(badReceiver, value); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // expected - } catch (IllegalAccessException e) { - throw new RuntimeException("Expected IllegalArgumentException but got: " + e.getMessage(), e); - } + assertThrows(IllegalArgumentException.class, () -> f.set(badReceiver, value)); // set the field with a bad receiver with a bad value Object badValue = new NegativeTest(); - try { - f.set(badReceiver, badValue); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // expected - } catch (IllegalAccessException e) { - throw new RuntimeException("Expected IllegalArgumentException but got: " + e.getMessage(), e); - } + assertThrows(IllegalArgumentException.class, () -> f.set(badReceiver, badValue)); // set the field with a null receiver with a good value - try { - f.set(null, value); - fail("expected NullPointerException"); - } catch (NullPointerException e) { - // expected - } catch (IllegalAccessException e) { - throw new RuntimeException("Expected NullPointerException but got: " + e.getMessage(), e); - } + assertThrows(NullPointerException.class, () -> f.set(null, value)); // set the field with a null receiver with a bad value - try { - f.set(null, badValue); - fail("expected NullPointerException"); - } catch (NullPointerException e) { - // expected - } catch (IllegalAccessException e) { - throw new RuntimeException("Expected NullPointerException but got: " + e.getMessage(), e); - } + assertThrows(NullPointerException.class, () -> f.set(null, badValue)); Class fType = f.getType(); if (fType.isPrimitive()) { // test bad receiver - try { + assertThrows(IllegalArgumentException.class, () -> { switch (fType.descriptorString()) { case "B" -> f.setByte(badReceiver, ((Byte) value).byteValue()); case "C" -> f.setChar(badReceiver, ((Character) value).charValue()); @@ -555,12 +481,9 @@ public class NegativeTest { case "S" -> f.setShort(badReceiver, ((Short) value).shortValue()); case "Z" -> f.setBoolean(badReceiver, ((Boolean) value).booleanValue()); } - } catch (IllegalArgumentException e) { - } catch (IllegalAccessException e) { - throw new RuntimeException("Expected IllegalArgumentException but got: " + e.getMessage(), e); - } + }); // test null receiver - try { + assertThrows(NullPointerException.class, () -> { switch (fType.descriptorString()) { case "B" -> f.setByte(null, ((Byte) value).byteValue()); case "C" -> f.setChar(null, ((Character) value).charValue()); @@ -571,11 +494,7 @@ public class NegativeTest { case "S" -> f.setShort(null, ((Short) value).shortValue()); case "Z" -> f.setBoolean(null, ((Boolean) value).booleanValue()); } - } catch (NullPointerException e) { - // expected - } catch (IllegalAccessException e) { - throw new RuntimeException("Expected NullPointerException but got: " + e.getMessage(), e); - } + }); } } } diff --git a/test/jdk/java/lang/reflect/Generics/ThreadSafety.java b/test/jdk/java/lang/reflect/Generics/ThreadSafety.java index 1aa1a4e9386..81ca2ae488a 100644 --- a/test/jdk/java/lang/reflect/Generics/ThreadSafety.java +++ b/test/jdk/java/lang/reflect/Generics/ThreadSafety.java @@ -21,17 +21,16 @@ * questions. */ -/** +/* * @test * @bug 8062771 8016236 * @summary Test publication of Class objects via a data race - * @run testng ThreadSafety + * @run junit ThreadSafety */ import java.io.File; import java.net.URL; import java.net.URLClassLoader; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; import java.util.concurrent.BrokenBarrierException; @@ -43,8 +42,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; import static java.util.concurrent.TimeUnit.SECONDS; -import static org.testng.Assert.*; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * A test resulting from an attempt to repro this failure (in guice): diff --git a/test/jdk/java/lang/reflect/IllegalArgumentsTest.java b/test/jdk/java/lang/reflect/IllegalArgumentsTest.java index 7260c04c10a..58115523a3d 100644 --- a/test/jdk/java/lang/reflect/IllegalArgumentsTest.java +++ b/test/jdk/java/lang/reflect/IllegalArgumentsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -25,12 +25,14 @@ * @test * @bug 8277964 * @summary Test IllegalArgumentException be thrown when an argument is invalid - * @run testng/othervm/timeout=720 IllegalArgumentsTest + * @comment Avoid using framework utilities; this is effectively a runtime test + * sensitive to stack traces + * @run junit/othervm/timeout=720 IllegalArgumentsTest */ import java.lang.reflect.Constructor; import java.lang.reflect.Method; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; public class IllegalArgumentsTest { static class T { diff --git a/test/jdk/java/lang/reflect/Method/MethodArityLimit.java b/test/jdk/java/lang/reflect/Method/MethodArityLimit.java index 536b3a5e317..05d4ef609ef 100644 --- a/test/jdk/java/lang/reflect/Method/MethodArityLimit.java +++ b/test/jdk/java/lang/reflect/Method/MethodArityLimit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -24,18 +24,16 @@ /* * @test * @bug 8271820 - * @run testng/othervm MethodArityLimit + * @run junit/othervm MethodArityLimit * @summary Method exceeds the method handle arity limit (255). */ -import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; +import static org.junit.jupiter.api.Assertions.*; public class MethodArityLimit { @Test @@ -74,12 +72,10 @@ public class MethodArityLimit { 106L, 107L, 108L, 109L, 110L, 111L, 112L, 113L, 114L, 115L, 116L, 117L, 118L, 119L, 120L, 121L, 122L, 123L, 124L, 125L, 126L, 127); - assertEquals(resultViaMethod, 127); + assertEquals(127, resultViaMethod); - try { - MethodHandle mh = MethodHandles.lookup().unreflect(m); - fail("should fail in creating the method handle"); - } catch (IllegalArgumentException e) {} + var lookup = MethodHandles.lookup(); + assertThrows(IllegalArgumentException.class, () -> lookup.unreflect(m)); } public static long f(long a0, long a1, long a2, long a3, long a4, long a5, diff --git a/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java b/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java index b7e6b52b5dc..af72d8dec58 100644 --- a/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java +++ b/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ * @bug 8271820 8300924 * @modules java.base/jdk.internal.reflect * @summary Test compliance of ConstructorAccessor, FieldAccessor, MethodAccessor implementations - * @run testng/othervm --add-exports java.base/jdk.internal.reflect=ALL-UNNAMED -XX:-ShowCodeDetailsInExceptionMessages MethodHandleAccessorsTest + * @run junit/othervm -XX:-ShowCodeDetailsInExceptionMessages MethodHandleAccessorsTest */ import jdk.internal.reflect.ConstructorAccessor; @@ -44,8 +44,11 @@ import java.util.Objects; import java.util.function.IntUnaryOperator; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertThrows; public class MethodHandleAccessorsTest { public static void public_static_V() {} @@ -446,9 +449,7 @@ public class MethodHandleAccessorsTest { new InvocationTargetException(new IllegalArgumentException("IAE")) }; - - @DataProvider(name = "testNoArgMethods") - private Object[][] testNoArgMethods() { + private static Object[][] testNoArgMethods() { MethodHandleAccessorsTest inst = new MethodHandleAccessorsTest(); Object[] emptyArgs = new Object[]{}; return new Object[][] { @@ -468,8 +469,7 @@ public class MethodHandleAccessorsTest { }; } - @DataProvider(name = "testOneArgMethods") - private Object[][] testOneArgMethods() { + private static Object[][] testOneArgMethods() { MethodHandleAccessorsTest inst = new MethodHandleAccessorsTest(); Object wrongInst = new Object(); return new Object[][]{ @@ -497,8 +497,7 @@ public class MethodHandleAccessorsTest { }; } - @DataProvider(name = "testMultiArgMethods") - private Object[][] testMultiArgMethods() { + private static Object[][] testMultiArgMethods() { MethodHandleAccessorsTest inst = new MethodHandleAccessorsTest(); Class[] params_L3 = new Class[] { Object.class, Object.class, Object.class}; Class[] params_L4 = new Class[] { Object.class, Object.class, Object.class, Object.class}; @@ -515,8 +514,7 @@ public class MethodHandleAccessorsTest { }; } - @DataProvider(name = "testMethodsWithVarargs") - private Object[][] testMethodsWithVarargs() { + private static Object[][] testMethodsWithVarargs() { Class[] paramTypes = new Class[] { int[].class }; Class[] I_paramTypes = new Class[] { int.class, int[].class }; Class[] L_paramTypes = new Class[] { String.class, String[].class }; @@ -533,32 +531,35 @@ public class MethodHandleAccessorsTest { }; } - @Test(dataProvider = "testNoArgMethods") + @ParameterizedTest + @MethodSource("testNoArgMethods") public void testNoArgMethod(String methodname, Object target, Object[] args, Object expectedReturn, Throwable[] expectedExpections) throws Exception { doTest(MethodHandleAccessorsTest.class.getDeclaredMethod(methodname), target, args, expectedReturn, expectedExpections); } - @Test(dataProvider = "testOneArgMethods") + @ParameterizedTest + @MethodSource("testOneArgMethods") public void testOneArgMethod(String methodname, Class paramType, Object target, Object[] args, Object expectedReturn, Throwable[] expectedExpections) throws Exception { doTest(MethodHandleAccessorsTest.class.getDeclaredMethod(methodname, paramType), target, args, expectedReturn, expectedExpections); } - @Test(dataProvider = "testMultiArgMethods") + @ParameterizedTest + @MethodSource("testMultiArgMethods") public void testMultiArgMethod(String methodname, Class[] paramTypes, Object target, Object[] args, Object expectedReturn, Throwable[] expectedExpections) throws Exception { doTest(MethodHandleAccessorsTest.class.getDeclaredMethod(methodname, paramTypes), target, args, expectedReturn, expectedExpections); } - @Test(dataProvider = "testMethodsWithVarargs") + @ParameterizedTest + @MethodSource("testMethodsWithVarargs") public void testMethodsWithVarargs(String methodname, Class[] paramTypes, Object target, Object[] args, Object expectedReturn, Throwable[] expectedExpections) throws Exception { doTest(MethodHandleAccessorsTest.class.getDeclaredMethod(methodname, paramTypes), target, args, expectedReturn, expectedExpections); } - @DataProvider(name = "testConstructors") - private Object[][] testConstructors() { + private static Object[][] testConstructors() { return new Object[][]{ new Object[]{null, new Object[]{}, new Public(), noException}, new Object[]{null, null, new Public(), noException}, @@ -584,13 +585,13 @@ public class MethodHandleAccessorsTest { }; } - @Test(dataProvider = "testConstructors") + @ParameterizedTest + @MethodSource("testConstructors") public void testPublicConstructors(Class[] paramTypes, Object[] args, Object expectedReturn, Throwable[] expectedExpections) throws Exception { doTest(Public.class.getDeclaredConstructor(paramTypes), args, expectedReturn, expectedExpections); } - @DataProvider(name = "testMultiArgConstructors") - private Object[][] testMultiArgConstructors() { + private static Object[][] testMultiArgConstructors() { Class[] params_L3 = new Class[] { Object.class, Object.class, Object.class}; Class[] params_L4 = new Class[] { Object.class, Object.class, Object.class, Object.class}; Object o = "arg"; @@ -602,7 +603,8 @@ public class MethodHandleAccessorsTest { }; } - @Test(dataProvider = "testMultiArgConstructors") + @ParameterizedTest + @MethodSource("testMultiArgConstructors") public void testMultiArgConstructors(Class[] paramTypes, Object[] args, Object expectedReturn, Throwable[] expectedExpections) throws Exception { doTest(Public.class.getDeclaredConstructor(paramTypes), args, expectedReturn, expectedExpections); } @@ -616,8 +618,7 @@ public class MethodHandleAccessorsTest { doTest(Abstract.class.getDeclaredConstructor(), null, null, new InstantiationException()); } - @DataProvider(name = "throwException") - private Object[][] throwException() { + private static Object[][] throwException() { return new Object[][]{ new Object[] {new NullPointerException("NPE"), wrapped_npe}, new Object[] {new IllegalArgumentException("IAE"), wrapped_iae}, @@ -629,7 +630,8 @@ public class MethodHandleAccessorsTest { * Test Method::invoke and Constructor::newInstance to wrap NPE/CCE/IAE * thrown by the member */ - @Test(dataProvider = "throwException") + @ParameterizedTest + @MethodSource("throwException") public void testInvocationTargetException(Throwable ex, Throwable[] expectedExpections) throws Exception { Object[] args = new Object[] { ex }; // test static method @@ -646,8 +648,7 @@ public class MethodHandleAccessorsTest { doTest(applyAsIntMethod, intUnaryOp, new Object[]{12}, 12); } - @DataProvider(name = "readAccess") - private Object[][] readAccess() { + private static Object[][] readAccess() { String wrongInst = new String(); return new Object[][]{ new Object[]{"i", new Public(100), 100, noException}, @@ -657,8 +658,7 @@ public class MethodHandleAccessorsTest { new Object[]{"b", wrongInst, 0, cannot_get_field}, }; } - @DataProvider(name = "writeAccess") - private Object[][] writeAccess() { + private static Object[][] writeAccess() { Object o = new Object(); byte b = 1; return new Object[][]{ @@ -673,14 +673,16 @@ public class MethodHandleAccessorsTest { }; } - @Test(dataProvider = "readAccess") + @ParameterizedTest + @MethodSource("readAccess") public void testFieldReadAccess(String name, Object target, Object expectedValue, Throwable[] expectedExpections) throws Exception { Field f = Public.class.getDeclaredField(name); f.setAccessible(true); doTest(f, target, expectedValue, expectedExpections); } - @Test(dataProvider = "writeAccess") + @ParameterizedTest + @MethodSource("writeAccess") public void testFieldWriteAccess(String name, Object target, Object oldValue, Object newValue, Throwable[] expectedExpections) throws Exception { Field f = Public.class.getDeclaredField(name); f.setAccessible(true); @@ -693,8 +695,6 @@ public class MethodHandleAccessorsTest { Field f = Public.class.getDeclaredField("STATIC_FINAL"); doTest(f, new Public(), 1, noException); - try { - f.setInt(null, 100); - } catch (IllegalAccessException e) { } + assertThrows(IllegalAccessException.class, () -> f.setInt(null, 100)); } } diff --git a/test/jdk/java/lang/reflect/Proxy/DefaultMethods.java b/test/jdk/java/lang/reflect/Proxy/DefaultMethods.java index 48bb67f405e..e494e623119 100644 --- a/test/jdk/java/lang/reflect/Proxy/DefaultMethods.java +++ b/test/jdk/java/lang/reflect/Proxy/DefaultMethods.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -23,19 +23,19 @@ import java.io.IOException; import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; -/** +/* * @test * @bug 8159746 - * @run testng DefaultMethods + * @run junit DefaultMethods * @summary Basic tests for Proxy::invokeSuper default method */ @@ -96,15 +96,11 @@ public class DefaultMethods { } private static Method findDefaultMethod(Class refc, Method m) { - try { - assertTrue(refc.isInterface()); + assertTrue(refc.isInterface()); - Method method = refc.getMethod(m.getName(), m.getParameterTypes()); - assertTrue(method.isDefault()); - return method; - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } + Method method = assertDoesNotThrow(() -> refc.getMethod(m.getName(), m.getParameterTypes())); + assertTrue(method.isDefault()); + return method; } @Test @@ -115,12 +111,11 @@ public class DefaultMethods { return InvocationHandler.invokeDefault(o, findDefaultMethod(I2.class, method), params); }); I1 i1 = (I1) proxy; - assertEquals(i1.m(), 20); + assertEquals(20, i1.m()); } // a default method is declared in one of the proxy interfaces - @DataProvider(name = "defaultMethods") - private Object[][] defaultMethods() { + private static Object[][] defaultMethods() { return new Object[][]{ new Object[]{new Class[]{I1.class, I2.class}, true, 10}, new Object[]{new Class[]{I1.class, I3.class}, true, 10}, @@ -133,13 +128,14 @@ public class DefaultMethods { }; } - @Test(dataProvider = "defaultMethods") + @ParameterizedTest + @MethodSource("defaultMethods") public void testDefaultMethod(Class[] intfs, boolean isDefault, int expected) throws Throwable { InvocationHandler ih = (proxy, method, params) -> { System.out.format("invoking %s with parameters: %s%n", method, Arrays.toString(params)); switch (method.getName()) { case "m": - assertTrue(method.isDefault() == isDefault); + assertEquals(isDefault, method.isDefault()); assertTrue(Arrays.stream(proxy.getClass().getInterfaces()) .anyMatch(intf -> method.getDeclaringClass() == intf), Arrays.toString(proxy.getClass().getInterfaces())); @@ -156,13 +152,12 @@ public class DefaultMethods { Object proxy = Proxy.newProxyInstance(DefaultMethods.class.getClassLoader(), intfs, ih); Method m = proxy.getClass().getMethod("m"); int result = (int)m.invoke(proxy); - assertEquals(result, expected); + assertEquals(expected, result); } // a default method may be declared in a proxy interface or // inherited from a superinterface of a proxy interface - @DataProvider(name = "supers") - private Object[][] supers() { + private static Object[][] supers() { return new Object[][]{ // invoke "m" implemented in the first proxy interface // same as the method passed to InvocationHandler::invoke @@ -188,7 +183,8 @@ public class DefaultMethods { }; } - @Test(dataProvider = "supers") + @ParameterizedTest + @MethodSource("supers") public void testSuper(Class[] intfs, Class proxyInterface, int expected) throws Throwable { final InvocationHandler ih = (proxy, method, params) -> { switch (method.getName()) { @@ -203,21 +199,21 @@ public class DefaultMethods { Object proxy = Proxy.newProxyInstance(loader, intfs, ih); if (proxyInterface == I1.class) { I1 i1 = (I1) proxy; - assertEquals(i1.m(), expected); + assertEquals(expected, i1.m()); } else if (proxyInterface == I2.class) { I2 i2 = (I2) proxy; - assertEquals(i2.m(), expected); + assertEquals(expected, i2.m()); } else if (proxyInterface == I3.class) { I3 i3 = (I3) proxy; - assertEquals(i3.m(), expected); + assertEquals(expected, i3.m()); } else if (proxyInterface == I4.class) { I4 i4 = (I4) proxy; - assertEquals(i4.m(), expected); + assertEquals(expected, i4.m()); } else { throw new UnsupportedOperationException(proxyInterface.toString()); } // invoke via InvocationHandler.invokeDefaultMethod directly - assertEquals(InvocationHandler.invokeDefault(proxy, proxyInterface.getMethod("m")), expected); + assertEquals(expected, InvocationHandler.invokeDefault(proxy, proxyInterface.getMethod("m"))); } // invoke I12 default methods with parameters and var args @@ -236,12 +232,12 @@ public class DefaultMethods { }; ClassLoader loader = DefaultMethods.class.getClassLoader(); I12 i12 = (I12) Proxy.newProxyInstance(loader, new Class[] { I12.class }, ih); - assertEquals(i12.sum(1, 2), 3); - assertEquals(i12.concat(1, 2, 3, 4), new Object[]{1, 2, 3, 4}); + assertEquals(3, i12.sum(1, 2)); + assertArrayEquals(new Object[]{1, 2, 3, 4}, i12.concat(1, 2, 3, 4)); Method m = I12.class.getMethod("concat", Object.class, Object[].class); assertTrue(m.isDefault()); - assertEquals(InvocationHandler.invokeDefault(i12, m, 100, new Object[] {"foo", true, "bar"}), - new Object[] {100, "foo", true, "bar"}); + assertArrayEquals(new Object[] {100, "foo", true, "bar"}, (Object[]) + InvocationHandler.invokeDefault(i12, m, 100, new Object[] {"foo", true, "bar"})); } // test a no-arg default method with and without arguments passed in the invocation @@ -250,13 +246,13 @@ public class DefaultMethods { ClassLoader loader = DefaultMethods.class.getClassLoader(); Object proxy = Proxy.newProxyInstance(loader, new Class[]{I4.class}, HANDLER); Method m1 = I4.class.getMethod("m"); - assertTrue(m1.getDeclaringClass() == I4.class); + assertSame(I4.class, m1.getDeclaringClass()); assertTrue(m1.isDefault()); InvocationHandler.invokeDefault(proxy, m1); InvocationHandler.invokeDefault(proxy, m1, new Object[0]); Method m2 = I4.class.getMethod("mix", int.class, String.class); - assertTrue(m1.getDeclaringClass() == I4.class); + assertSame(I4.class, m1.getDeclaringClass()); assertTrue(m1.isDefault()); InvocationHandler.invokeDefault(proxy, m2, Integer.valueOf(100), "foo"); } @@ -267,38 +263,37 @@ public class DefaultMethods { I3 proxy = (I3)Proxy.newProxyInstance(loader, new Class[]{I3.class}, HANDLER); Method m = I3.class.getMethod("m3", String[].class); assertTrue(m.isVarArgs() && m.isDefault()); - assertEquals(proxy.m3("a", "b", "cde"), 5); - assertEquals(InvocationHandler.invokeDefault(proxy, m, (Object)new String[] { "a", "bc" }), 3); + assertEquals(5, proxy.m3("a", "b", "cde")); + assertEquals(3, InvocationHandler.invokeDefault(proxy, m, (Object)new String[] { "a", "bc" })); } /* * Invoke I12::m which is an abstract method */ - @Test(expectedExceptions = {IllegalArgumentException.class}) + @Test public void invokeAbstractMethod() throws Exception { ClassLoader loader = DefaultMethods.class.getClassLoader(); I12 proxy = (I12) Proxy.newProxyInstance(loader, new Class[]{I12.class}, HANDLER); Method method = I12.class.getMethod("m"); - assertTrue(method.getDeclaringClass() == I12.class); + assertSame(I12.class, method.getDeclaringClass()); assertFalse(method.isDefault()); - proxy.m(); + assertThrows(IllegalArgumentException.class, () -> proxy.m()); } /* * Invoke a non proxy (default) method with parameters */ - @Test(expectedExceptions = {IllegalArgumentException.class}) + @Test public void invokeNonProxyMethod() throws Throwable { ClassLoader loader = DefaultMethods.class.getClassLoader(); I3 proxy = (I3) Proxy.newProxyInstance(loader, new Class[]{I3.class}, HANDLER); Method m = I4.class.getMethod("mix", int.class, String.class); assertTrue(m.isDefault()); - InvocationHandler.invokeDefault(proxy, m); + assertThrows(IllegalArgumentException.class, () -> InvocationHandler.invokeDefault(proxy, m)); } // negative cases - @DataProvider(name = "negativeCases") - private Object[][] negativeCases() { + private static Object[][] negativeCases() { return new Object[][]{ // I4::m overrides I1::m and I2::m new Object[] { new Class[]{I4.class}, I1.class, "m" }, @@ -315,25 +310,20 @@ public class DefaultMethods { }; } - @Test(dataProvider = "negativeCases", expectedExceptions = {IllegalArgumentException.class}) + @ParameterizedTest + @MethodSource("negativeCases") public void testNegativeCase(Class[] interfaces, Class defc, String name) throws Throwable { ClassLoader loader = DefaultMethods.class.getClassLoader(); Object proxy = Proxy.newProxyInstance(loader, interfaces, HANDLER); - try { - Method method = defc.getDeclaredMethod(name); - InvocationHandler.invokeDefault(proxy, method); - } catch (Throwable e) { - System.out.format("%s method %s::%s exception thrown: %s%n", - Arrays.toString(interfaces), defc.getName(), name, e.getMessage()); - throw e; - } + Method method = defc.getDeclaredMethod(name); + assertThrows(IllegalArgumentException.class, () -> + InvocationHandler.invokeDefault(proxy, method)); } - @DataProvider(name = "illegalArguments") - private Object[][] illegalArguments() { - return new Object[][] { - new Object[] { new Object[0]}, + private static Object[] illegalArguments() { + return new Object[] { + new Object[] { null }, new Object[] { new Object[] { 100 }}, new Object[] { new Object[] { 100, "foo", 100 }}, new Object[] { new Object[] { 100L, "foo" }}, @@ -342,21 +332,18 @@ public class DefaultMethods { }; } - @Test(dataProvider = "illegalArguments", expectedExceptions = {IllegalArgumentException.class}) + @ParameterizedTest + @MethodSource("illegalArguments") public void testIllegalArgument(Object[] args) throws Throwable { ClassLoader loader = DefaultMethods.class.getClassLoader(); I4 proxy = (I4)Proxy.newProxyInstance(loader, new Class[]{I4.class}, HANDLER); Method m = I4.class.getMethod("mix", int.class, String.class); assertTrue(m.isDefault()); - if (args.length == 0) { - // substitute empty args with null since @DataProvider doesn't allow null array - args = null; - } - InvocationHandler.invokeDefault(proxy, m, args); + assertThrows(IllegalArgumentException.class, () -> + InvocationHandler.invokeDefault(proxy, m, args)); } - @DataProvider(name = "throwables") - private Object[][] throwables() { + private static Object[][] throwables() { return new Object[][] { new Object[] { new IOException() }, new Object[] { new IllegalArgumentException() }, @@ -367,16 +354,14 @@ public class DefaultMethods { }; } - @Test(dataProvider = "throwables") + @ParameterizedTest + @MethodSource("throwables") public void testInvocationException(Throwable exception) throws Throwable { ClassLoader loader = DefaultMethods.class.getClassLoader(); IX proxy = (IX)Proxy.newProxyInstance(loader, new Class[]{IX.class}, HANDLER); Method m = IX.class.getMethod("doThrow", Throwable.class); - try { - InvocationHandler.invokeDefault(proxy, m, exception); - } catch (Throwable e) { - assertEquals(e, exception); - } + assertSame(exception, assertThrows(Throwable.class, () -> + InvocationHandler.invokeDefault(proxy, m, exception))); } private static final InvocationHandler HANDLER = (proxy, method, params) -> { diff --git a/test/jdk/java/lang/reflect/Proxy/HiddenProxyInterface.java b/test/jdk/java/lang/reflect/Proxy/HiddenProxyInterface.java index e52d3671ab6..fbaf9db6325 100644 --- a/test/jdk/java/lang/reflect/Proxy/HiddenProxyInterface.java +++ b/test/jdk/java/lang/reflect/Proxy/HiddenProxyInterface.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -24,7 +24,7 @@ /* * @test * @bug 8250219 - * @run testng HiddenProxyInterface + * @run junit HiddenProxyInterface */ import java.lang.invoke.MethodHandles; @@ -35,8 +35,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; public class HiddenProxyInterface { private static final Path CLASSES_DIR = Paths.get(System.getProperty("test.classes")); @@ -45,18 +45,19 @@ public class HiddenProxyInterface { void m(); } - @Test(expectedExceptions = { IllegalArgumentException.class }) + @Test public void testHiddenInterface() throws Exception { Path classFile = CLASSES_DIR.resolve("HiddenProxyInterface$Intf.class"); byte[] bytes = Files.readAllBytes(classFile); Class hiddenIntf = MethodHandles.lookup().defineHiddenClass(bytes, false).lookupClass(); - Proxy.newProxyInstance(HiddenProxyInterface.class.getClassLoader(), + assertThrows(IllegalArgumentException.class, () -> Proxy.newProxyInstance( + HiddenProxyInterface.class.getClassLoader(), new Class[]{ hiddenIntf }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } - }); + })); } } diff --git a/test/jdk/java/lang/reflect/Proxy/LazyInitializationTest.java b/test/jdk/java/lang/reflect/Proxy/LazyInitializationTest.java index cb65904cfdb..2a223873275 100644 --- a/test/jdk/java/lang/reflect/Proxy/LazyInitializationTest.java +++ b/test/jdk/java/lang/reflect/Proxy/LazyInitializationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -23,14 +23,15 @@ import java.lang.reflect.Proxy; -import org.testng.Assert; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; /* * @test * @bug 8285401 * @summary Avoid initialization of parameter types in proxy construction - * @run testng LazyInitializationTest + * @run junit LazyInitializationTest */ public final class LazyInitializationTest { private static volatile boolean initialized = false; @@ -50,9 +51,9 @@ public final class LazyInitializationTest { Intf value = (Intf) Proxy.newProxyInstance(LazyInitializationTest.class.getClassLoader(), new Class[]{ Intf.class }, (proxy, method, args) -> null); - Assert.assertFalse(initialized, "parameter type initialized unnecessarily"); + assertFalse(initialized, "parameter type initialized unnecessarily"); value.m(new Parameter()); - Assert.assertTrue(initialized, "parameter type initialized after instantiation"); + assertTrue(initialized, "parameter type initialized after instantiation"); } } diff --git a/test/jdk/java/lang/reflect/Proxy/ProxyClassAccessTest.java b/test/jdk/java/lang/reflect/Proxy/ProxyClassAccessTest.java index 50fbd150ec7..265f9ce7104 100644 --- a/test/jdk/java/lang/reflect/Proxy/ProxyClassAccessTest.java +++ b/test/jdk/java/lang/reflect/Proxy/ProxyClassAccessTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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,17 +32,17 @@ import java.util.List; import jdk.test.lib.compiler.CompilerUtils; import static jdk.test.lib.process.ProcessTools.executeTestJava; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; -/** +/* * @test * @library /test/lib * @modules jdk.compiler * @build ProxyClassAccessTest q.NP * jdk.test.lib.compiler.CompilerUtils - * @run testng ProxyClassAccessTest + * @run junit ProxyClassAccessTest * @summary Driver for testing proxy class doesn't have access to * types referenced by proxy interfaces */ @@ -61,8 +61,8 @@ public class ProxyClassAccessTest { /** * Compiles all modules used by the test */ - @BeforeTest - public void compileAll() throws Exception { + @BeforeAll + public static void compileAll() throws Exception { for (String mn : modules) { Path msrc = SRC_DIR.resolve(mn); assertTrue(CompilerUtils.compile(msrc, MODS_DIR, "--module-source-path", SRC_DIR.toString())); @@ -80,7 +80,7 @@ public class ProxyClassAccessTest { .errorTo(System.out) .getExitValue(); - assertTrue(exitValue == 0); + assertEquals(0, exitValue); } /** @@ -105,16 +105,10 @@ public class ProxyClassAccessTest { } private void checkIAE(ClassLoader loader, Class[] interfaces) throws Throwable { - try { - Proxy.getProxyClass(loader, interfaces); - throw new RuntimeException("Expected IllegalArgumentException thrown"); - } catch (IllegalArgumentException e) {} + assertThrows(IllegalArgumentException.class, () -> Proxy.getProxyClass(loader, interfaces)); - try { - Proxy.newProxyInstance(loader, interfaces, - (proxy, m, params) -> { throw new RuntimeException(m.toString()); }); - throw new RuntimeException("Expected IllegalArgumentException thrown"); - } catch (IllegalArgumentException e) {} + assertThrows(IllegalArgumentException.class, () -> Proxy.newProxyInstance(loader, interfaces, + (proxy, m, params) -> { throw new RuntimeException(m.toString()); })); } } diff --git a/test/jdk/java/lang/reflect/Proxy/ProxyLayerTest.java b/test/jdk/java/lang/reflect/Proxy/ProxyLayerTest.java index 85ebf922a20..d2708e18c0e 100644 --- a/test/jdk/java/lang/reflect/Proxy/ProxyLayerTest.java +++ b/test/jdk/java/lang/reflect/Proxy/ProxyLayerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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,17 +32,18 @@ import java.util.Arrays; import jdk.test.lib.compiler.CompilerUtils; import static jdk.test.lib.process.ProcessTools.executeTestJava; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; -/** +/* * @test * @library /test/lib * @modules jdk.compiler * @build ProxyTest jdk.test.lib.process.ProcessTools * jdk.test.lib.compiler.CompilerUtils - * @run testng ProxyLayerTest + * @run junit ProxyLayerTest * @summary Test proxies to implement interfaces in a layer */ @@ -62,8 +63,8 @@ public class ProxyLayerTest { /** * Compiles all modules used by the test */ - @BeforeTest - public void compileAll() throws Exception { + @BeforeAll + public static void compileAll() throws Exception { for (String mn : modules) { Path msrc = SRC_DIR.resolve(mn); assertTrue(CompilerUtils.compile(msrc, MODS_DIR, "--module-source-path", SRC_DIR.toString())); @@ -102,7 +103,7 @@ public class ProxyLayerTest { assertTrue(proxyClass.getModule().isNamed()); assertTrue(pkg.isSealed()); assertTrue(proxyClass.getModule().isExported(pkg.getName())); - assertEquals(proxyClass.getModule().getLayer(), null); + assertNull(proxyClass.getModule().getLayer()); } /** @@ -134,7 +135,7 @@ public class ProxyLayerTest { assertTrue(proxyClass.getModule().isNamed()); assertTrue(pkg.isSealed()); assertFalse(proxyClass.getModule().isExported(pkg.getName())); - assertEquals(proxyClass.getModule().getLayer(), null); + assertNull(proxyClass.getModule().getLayer()); } /** @@ -164,15 +165,8 @@ public class ProxyLayerTest { } private void checkIAE(ClassLoader loader, Class[] interfaces) { - try { - Proxy.getProxyClass(loader, interfaces); - throw new RuntimeException("Expected IllegalArgumentException thrown"); - } catch (IllegalArgumentException e) {} - - try { - Proxy.newProxyInstance(loader, interfaces, handler); - throw new RuntimeException("Expected IllegalArgumentException thrown"); - } catch (IllegalArgumentException e) {} + assertThrows(IllegalArgumentException.class, () -> Proxy.getProxyClass(loader, interfaces)); + assertThrows(IllegalArgumentException.class, () -> Proxy.newProxyInstance(loader, interfaces, handler)); } private final static InvocationHandler handler = diff --git a/test/jdk/java/lang/reflect/Proxy/ProxyTest.java b/test/jdk/java/lang/reflect/Proxy/ProxyTest.java index 811471585a9..1da6a10260a 100644 --- a/test/jdk/java/lang/reflect/Proxy/ProxyTest.java +++ b/test/jdk/java/lang/reflect/Proxy/ProxyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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,17 +29,17 @@ import java.util.List; import jdk.test.lib.compiler.CompilerUtils; import static jdk.test.lib.process.ProcessTools.executeTestJava; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; -/** +/* * @test * @library /test/lib * @modules jdk.compiler * @build ProxyTest q.U * jdk.test.lib.compiler.CompilerUtils - * @run testng ProxyTest + * @run junit ProxyTest * @summary Driver for testing proxies accessing interfaces in named modules */ @@ -59,8 +59,8 @@ public class ProxyTest { /** * Compiles all modules used by the test */ - @BeforeTest - public void compileAll() throws Exception { + @BeforeAll + public static void compileAll() throws Exception { for (String mn : modules) { Path msrc = SRC_DIR.resolve(mn); assertTrue(CompilerUtils.compile(msrc, MODS_DIR, "--module-source-path", SRC_DIR.toString())); @@ -79,7 +79,7 @@ public class ProxyTest { .errorTo(System.out) .getExitValue(); - assertTrue(exitValue == 0); + assertEquals(0, exitValue); } /** @@ -95,6 +95,6 @@ public class ProxyTest { .errorTo(System.out) .getExitValue(); - assertTrue(exitValue == 0); + assertEquals(0, exitValue); } } diff --git a/test/jdk/java/lang/reflect/Proxy/SealedInterfaceTest.java b/test/jdk/java/lang/reflect/Proxy/SealedInterfaceTest.java index 1337248f817..822c5d83319 100644 --- a/test/jdk/java/lang/reflect/Proxy/SealedInterfaceTest.java +++ b/test/jdk/java/lang/reflect/Proxy/SealedInterfaceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -24,15 +24,15 @@ /* * @test * @bug 8269351 - * @run testng SealedInterfaceTest + * @run junit SealedInterfaceTest */ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; public class SealedInterfaceTest { sealed interface Intf permits NonSealedInterface { @@ -43,16 +43,17 @@ public class SealedInterfaceTest { void m2(); } - @Test(expectedExceptions = { IllegalArgumentException.class }) + @Test public void testSealedInterface() { - Proxy.newProxyInstance(SealedInterfaceTest.class.getClassLoader(), + assertThrows(IllegalArgumentException.class, () -> Proxy.newProxyInstance( + SealedInterfaceTest.class.getClassLoader(), new Class[]{ Intf.class }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } - }); + })); } @Test diff --git a/test/jdk/java/lang/reflect/Proxy/TestVarArgs.java b/test/jdk/java/lang/reflect/Proxy/TestVarArgs.java index a472f75d4b7..b16831c7867 100644 --- a/test/jdk/java/lang/reflect/Proxy/TestVarArgs.java +++ b/test/jdk/java/lang/reflect/Proxy/TestVarArgs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -21,10 +21,10 @@ * questions. */ -/** +/* * @test * @bug 8022795 - * @run testng TestVarArgs + * @run junit TestVarArgs * @summary Verify if a method defined in a proxy interface has ACC_VARARGS set */ @@ -34,11 +34,10 @@ import java.lang.invoke.MethodType; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import java.util.Arrays; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class TestVarArgs { interface I { @@ -48,8 +47,7 @@ public class TestVarArgs { Object call(Object[] values); } - @DataProvider(name = "proxyInterfaces") - public Object[][] proxyInterfaces() { + public static Object[][] proxyInterfaces() { return new Object[][] { { new Class[] { I.class }, true}, { new Class[] { J.class }, false}, @@ -58,11 +56,12 @@ public class TestVarArgs { }; } - @Test(dataProvider = "proxyInterfaces") + @ParameterizedTest + @MethodSource("proxyInterfaces") public void testMethod(Class[] proxyInterfaces, boolean isVarArgs) throws Throwable { // check if the first proxy interface with the method named "call" declares var result Method m = proxyInterfaces[0].getMethod("call", Object[].class); - assertTrue(m.isVarArgs() == isVarArgs); + assertEquals(isVarArgs, m.isVarArgs()); // the method in the generated proxy class should match the method // declared in the proxy interface @@ -71,19 +70,19 @@ public class TestVarArgs { Class proxyClass = proxy.getClass(); assertTrue(Proxy.isProxyClass(proxyClass)); Method method = proxyClass.getMethod("call", Object[].class); - assertTrue(method.isVarArgs() == isVarArgs); + assertEquals(isVarArgs, method.isVarArgs()); - Object params = new Object[] { "foo", "bar", "goo" }; - Object result; + Object[] params = new Object[] { "foo", "bar", "goo" }; + Object[] result; // test reflection - result = method.invoke(proxy, params); - assertEquals(result, params); + result = (Object[]) method.invoke(proxy, new Object[] {params}); + assertEquals(params, result); // test method handle MethodHandle mh = MethodHandles.lookup().findVirtual(proxyClass, "call", MethodType.methodType(Object.class, Object[].class)); - assertTrue(mh.isVarargsCollector() == isVarArgs); + assertEquals(isVarArgs, mh.isVarargsCollector()); MethodHandle mhVarArity = mh; MethodHandle mhFixedArity = mh; if (isVarArgs) { @@ -91,18 +90,18 @@ public class TestVarArgs { } else { mhVarArity = mh.asVarargsCollector(Object[].class); } - result = mhVarArity.invoke(proxy, "foo", "bar", "goo"); - assertEquals(result, params); + result = (Object[]) mhVarArity.invoke(proxy, "foo", "bar", "goo"); + assertArrayEquals(params, result); - result = mhFixedArity.invoke(proxy, params); - assertEquals(result, params); + result = (Object[]) mhFixedArity.invoke(proxy, params); + assertArrayEquals(params, result); if (!isVarArgs) { MethodType mt = MethodType.methodType(Object.class, Object.class, String.class, String.class, String.class); mh = mh.asVarargsCollector(Object[].class).asType(mt); } - result = mh.invoke(proxy, "foo", "bar", "goo"); - assertEquals(result, params); + result = (Object[]) mh.invoke(proxy, "foo", "bar", "goo"); + assertArrayEquals(params, result); } private static final InvocationHandler IH = new InvocationHandler() { diff --git a/test/jdk/java/lang/reflect/Proxy/nonPublicProxy/DefaultMethodProxy.java b/test/jdk/java/lang/reflect/Proxy/nonPublicProxy/DefaultMethodProxy.java index daf6f656aab..a304063638c 100644 --- a/test/jdk/java/lang/reflect/Proxy/nonPublicProxy/DefaultMethodProxy.java +++ b/test/jdk/java/lang/reflect/Proxy/nonPublicProxy/DefaultMethodProxy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -25,16 +25,17 @@ import java.lang.reflect.*; import java.util.Arrays; import java.util.stream.Collectors; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test * @bug 8159746 * @summary Test invoking a default method in a non-public proxy interface * @build p.Foo p.Bar p.ProxyMaker - * @run testng DefaultMethodProxy + * @run junit DefaultMethodProxy */ public class DefaultMethodProxy { public interface I { @@ -42,7 +43,7 @@ public class DefaultMethodProxy { } @Test - public static void publicInterface() throws ReflectiveOperationException { + public void publicInterface() throws ReflectiveOperationException { // create a proxy instance of a public proxy interface should succeed Proxy proxy = (Proxy)Proxy.newProxyInstance(DefaultMethodProxy.class.getClassLoader(), new Class[] { I.class }, IH); @@ -50,11 +51,9 @@ public class DefaultMethodProxy { testDefaultMethod(proxy, "I"); // can get the invocation handler - assertTrue(Proxy.getInvocationHandler(proxy) == IH); + assertSame(IH, Proxy.getInvocationHandler(proxy)); } - - @DataProvider(name = "nonPublicIntfs") private static Object[][] nonPublicIntfs() throws ClassNotFoundException { Class fooClass = Class.forName("p.Foo"); Class barClass = Class.forName("p.Bar"); @@ -65,8 +64,9 @@ public class DefaultMethodProxy { }; } - @Test(dataProvider = "nonPublicIntfs") - public static void hasPackageAccess(Class[] intfs, String expected) throws ReflectiveOperationException { + @ParameterizedTest + @MethodSource("nonPublicIntfs") + public void hasPackageAccess(Class[] intfs, String expected) throws ReflectiveOperationException { Proxy proxy = (Proxy)Proxy.newProxyInstance(DefaultMethodProxy.class.getClassLoader(), intfs, IH); testDefaultMethod(proxy, expected); @@ -75,19 +75,13 @@ public class DefaultMethodProxy { } // IAE thrown at invocation time - @Test(dataProvider = "nonPublicIntfs", expectedExceptions = {IllegalAccessException.class}) - public static void noPackageAccess(Class[] intfs, String ignored) throws Throwable { + @ParameterizedTest + @MethodSource("nonPublicIntfs") + public void noPackageAccess(Class[] intfs, String ignored) throws Throwable { Proxy proxy = (Proxy)Proxy.newProxyInstance(DefaultMethodProxy.class.getClassLoader(), intfs, IH_NO_ACCESS); - try { - testDefaultMethod(proxy, "dummy"); - } catch (InvocationTargetException e) { - // unwrap the exception - if (e.getCause() instanceof UndeclaredThrowableException) { - Throwable cause = e.getCause(); - throw cause.getCause(); - } - throw e; - } + InvocationTargetException ite = assertThrows(InvocationTargetException.class, () -> testDefaultMethod(proxy, "dummy")); + var ute = assertInstanceOf(UndeclaredThrowableException.class, ite.getCause(), "unwrap the InvocationTargetException"); + assertInstanceOf(IllegalAccessException.class, ute.getCause(), "unwrap the UndeclaredThrowableException"); } /* diff --git a/test/jdk/java/lang/reflect/annotationSharing/AnnotationSharing.java b/test/jdk/java/lang/reflect/annotationSharing/AnnotationSharing.java index 98a13d6416c..b29c74637d6 100644 --- a/test/jdk/java/lang/reflect/annotationSharing/AnnotationSharing.java +++ b/test/jdk/java/lang/reflect/annotationSharing/AnnotationSharing.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -27,13 +27,13 @@ * @summary Test sharing of annotations between Executable/Field instances. * Sharing should not be noticeable when performing mutating * operations. - * @run testng AnnotationSharing + * @run junit AnnotationSharing */ import java.lang.annotation.*; import java.lang.reflect.*; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; public class AnnotationSharing { @Test diff --git a/test/jdk/java/lang/reflect/callerCache/CustomLoaderTest.java b/test/jdk/java/lang/reflect/callerCache/CustomLoaderTest.java index f3a429027d9..6951cc0829c 100644 --- a/test/jdk/java/lang/reflect/callerCache/CustomLoaderTest.java +++ b/test/jdk/java/lang/reflect/callerCache/CustomLoaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -27,7 +27,7 @@ * @library /test/lib/ * @modules jdk.compiler * @build CustomLoaderTest jdk.test.lib.compiler.CompilerUtils - * @run testng/othervm CustomLoaderTest + * @run junit/othervm CustomLoaderTest * * @summary Test method whose parameter types and return type are not visible to the caller. */ @@ -42,27 +42,23 @@ import java.nio.file.Paths; import java.util.concurrent.atomic.AtomicInteger; import jdk.test.lib.compiler.CompilerUtils; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class CustomLoaderTest { private static final Path CLASSES = Paths.get("classes"); - @BeforeTest - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { String src = System.getProperty("test.src", "."); String classpath = System.getProperty("test.classes", "."); boolean rc = CompilerUtils.compile(Paths.get(src, "ReflectTest.java"), CLASSES, "-cp", classpath); if (!rc) { - throw new RuntimeException("fail compilation"); - } - try { - Class p = Class.forName("ReflectTest$P"); - fail("should not be visible to this loader"); - } catch (ClassNotFoundException e) { - e.printStackTrace(); + fail("fail compilation"); } + assertThrows(ClassNotFoundException.class, () -> Class.forName("ReflectTest$P"), + "should not be visible to this loader"); } @Test @@ -72,17 +68,17 @@ public class CustomLoaderTest { Method m1 = loader1.findMethod(); Method m2 = loader2.findMethod(); - assertTrue(m1.getDeclaringClass() != m2.getDeclaringClass()); + assertNotSame(m1.getDeclaringClass(), m2.getDeclaringClass()); - assertTrue(m1.getDeclaringClass() == loader1.c); - assertTrue(m2.getDeclaringClass() == loader2.c); + assertSame(loader1.c, m1.getDeclaringClass()); + assertSame(loader2.c, m2.getDeclaringClass()); Object o1 = m1.invoke(loader1.c.newInstance(), loader1.p.newInstance(), loader1.q.newInstance()); Object o2 = m2.invoke(loader2.c.newInstance(), loader2.p.newInstance(), loader2.q.newInstance()); - assertTrue(o1.getClass() != o2.getClass()); - assertTrue(o1.getClass() == loader1.r); - assertTrue(o2.getClass() == loader2.r); + assertNotSame(o1.getClass(), o2.getClass()); + assertSame(loader1.r, o1.getClass()); + assertSame(loader2.r, o2.getClass()); } static class TestLoader extends URLClassLoader { diff --git a/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java b/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java index b640a5e2150..4513c698a54 100644 --- a/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java +++ b/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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,7 +29,7 @@ * @library /test/lib/ * @modules jdk.compiler * @build ReflectionCallerCacheTest Members jdk.test.lib.compiler.CompilerUtils - * @run testng/othervm ReflectionCallerCacheTest + * @run junit/othervm ReflectionCallerCacheTest */ import java.io.IOException; @@ -44,16 +44,16 @@ import java.util.concurrent.Callable; import jdk.test.lib.compiler.CompilerUtils; import jdk.test.lib.util.ForceGC; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class ReflectionCallerCacheTest { private static final Path CLASSES = Paths.get("classes"); private static final ReflectionCallerCacheTest TEST = new ReflectionCallerCacheTest(); - @BeforeTest - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { String src = System.getProperty("test.src", "."); String classpath = System.getProperty("test.classes", "."); boolean rc = CompilerUtils.compile(Paths.get(src, "AccessTest.java"), CLASSES, "-cp", classpath); @@ -61,8 +61,8 @@ public class ReflectionCallerCacheTest { throw new RuntimeException("fail compilation"); } } - @DataProvider(name = "memberAccess") - public Object[][] memberAccess() { + + public static Object[][] memberAccess() { return new Object[][] { { "AccessTest$PublicConstructor" }, { "AccessTest$PublicMethod" }, @@ -102,8 +102,9 @@ public class ReflectionCallerCacheTest { } } - @Test(dataProvider = "memberAccess") - private void load(String classname) throws Exception { + @ParameterizedTest + @MethodSource("memberAccess") + void load(String classname) throws Exception { WeakReference weakLoader = loadAndRunClass(classname); // Force garbage collection to trigger unloading of class loader diff --git a/test/jdk/java/lang/reflect/records/CheckEqualityIsBasedOnFields.java b/test/jdk/java/lang/reflect/records/CheckEqualityIsBasedOnFields.java index b5c438e4f9a..c3e4431bd84 100644 --- a/test/jdk/java/lang/reflect/records/CheckEqualityIsBasedOnFields.java +++ b/test/jdk/java/lang/reflect/records/CheckEqualityIsBasedOnFields.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -25,17 +25,15 @@ * @test * @bug 8257598 * @summary check that Record::equals uses the fields and not the accessors for the comparison - * @run testng CheckEqualityIsBasedOnFields + * @run junit CheckEqualityIsBasedOnFields */ import java.lang.reflect.Constructor; -import java.lang.reflect.Field; import java.lang.reflect.Method; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class CheckEqualityIsBasedOnFields { public record R01(boolean x) { @@ -91,8 +89,7 @@ public class CheckEqualityIsBasedOnFields { } } - @DataProvider(name = "recordData") - public Object[][] recordTypeAndExpectedValue() { + public static Object[][] recordTypeAndExpectedValue() { return new Object[][] { new Object[] { R01.class, boolean.class, new Object[]{true, false} }, new Object[] { R02.class, byte.class, new Object[]{(byte)0, (byte)1, (byte)2, (byte)3, (byte)4, (byte)5, @@ -112,7 +109,8 @@ public class CheckEqualityIsBasedOnFields { }; } - @Test(dataProvider = "recordData") + @ParameterizedTest + @MethodSource("recordTypeAndExpectedValue") public void testEqualsDoesntUseAccessors(Class clazz, Class componentClass, Object[] expectedXValues) throws Exception { Constructor ctor; Method getter, equalsMethod; @@ -125,8 +123,8 @@ public class CheckEqualityIsBasedOnFields { System.out.println(rec1.toString()); System.out.println(rec2.toString()); assertFalse((boolean) equalsMethod.invoke(rec1, rec2)); - assertNotEquals(expectedXValues[i], expectedXValues[i + expectedXValues.length / 2]); - assertEquals(getter.invoke(rec1), getter.invoke(rec2)); + assertNotEquals(expectedXValues[i + expectedXValues.length / 2], expectedXValues[i]); + assertEquals(getter.invoke(rec2), getter.invoke(rec1)); } } } diff --git a/test/jdk/java/lang/reflect/records/IsRecordTest.java b/test/jdk/java/lang/reflect/records/IsRecordTest.java index 4888e6d506e..153329a7832 100644 --- a/test/jdk/java/lang/reflect/records/IsRecordTest.java +++ b/test/jdk/java/lang/reflect/records/IsRecordTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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,32 +26,31 @@ * @bug 8255560 * @summary Class::isRecord should check that the current class is final and not abstract * @library /test/lib - * @run testng/othervm IsRecordTest + * @run junit/othervm IsRecordTest */ import java.lang.classfile.ClassFile; import java.lang.constant.ClassDesc; -import java.lang.reflect.AccessFlag; import java.util.List; import java.util.Map; import java.lang.classfile.attribute.RecordAttribute; import java.lang.classfile.attribute.RecordComponentInfo; import jdk.test.lib.ByteCodeLoader; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.lang.classfile.ClassFile.ACC_ABSTRACT; import static java.lang.classfile.ClassFile.ACC_FINAL; import static java.lang.constant.ConstantDescs.CD_int; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.*; public class IsRecordTest { - @DataProvider(name = "scenarios") - public Object[][] scenarios() { + public static Object[][] scenarios() { return new Object[][] { // isFinal, isAbstract, extendJLR, withRecAttr, expectIsRecord { false, false, false, true, false }, @@ -75,7 +74,8 @@ public class IsRecordTest { * iii) direct subclass of j.l.Record (or not), along with the presence or * absence of a record attribute. */ - @Test(dataProvider = "scenarios") + @ParameterizedTest + @MethodSource("scenarios") public void testDirectSubClass(boolean isFinal, boolean isAbstract, boolean extendsJLR, @@ -92,9 +92,8 @@ public class IsRecordTest { Class cls = ByteCodeLoader.load("C", classBytes); out.println("cls=%s, Record::isAssignable=%s, isRecord=%s" .formatted(cls, Record.class.isAssignableFrom(cls), cls.isRecord())); - assertEquals(cls.isRecord(), expectIsRecord); - var getRecordComponents = cls.getRecordComponents(); - assertTrue(expectIsRecord ? getRecordComponents != null : getRecordComponents == null); + assertEquals(expectIsRecord, cls.isRecord()); + assertEquals(expectIsRecord, cls.getRecordComponents() != null); } /** @@ -102,7 +101,8 @@ public class IsRecordTest { * along with the presence or absence of a record attribute, where the class has * a superclass whose superclass is j.l.Record. */ - @Test(dataProvider = "scenarios") + @ParameterizedTest + @MethodSource("scenarios") public void testIndirectSubClass(boolean isFinal, boolean isAbstract, boolean unused1, @@ -127,8 +127,8 @@ public class IsRecordTest { .formatted(cls, Record.class.isAssignableFrom(cls), cls.isRecord())); assertFalse(supFooCls.isRecord()); assertFalse(subFooCls.isRecord()); - assertEquals(supFooCls.getRecordComponents(), null); - assertEquals(subFooCls.getRecordComponents(), null); + assertNull(supFooCls.getRecordComponents()); + assertNull(subFooCls.getRecordComponents()); } /** Tests record-ness properties of traditionally compiled classes. */ @@ -137,23 +137,23 @@ public class IsRecordTest { out.println("\n--- testBasicRecords ---"); record EmptyRecord () { } assertTrue(EmptyRecord.class.isRecord()); - assertEquals(EmptyRecord.class.getRecordComponents().length, 0); + assertEquals(0, EmptyRecord.class.getRecordComponents().length); record FooRecord (int x) { } assertTrue(FooRecord.class.isRecord()); - assertTrue(FooRecord.class.getRecordComponents() != null); + assertNotNull(FooRecord.class.getRecordComponents()); final record FinalFooRecord (int x) { } assertTrue(FinalFooRecord.class.isRecord()); - assertTrue(FinalFooRecord.class.getRecordComponents() != null); + assertNotNull(FinalFooRecord.class.getRecordComponents()); class A { } assertFalse(A.class.isRecord()); - assertFalse(A.class.getRecordComponents() != null); + assertNull(A.class.getRecordComponents()); final class B { } assertFalse(B.class.isRecord()); - assertFalse(B.class.getRecordComponents() != null); + assertNull(B.class.getRecordComponents()); } // -- infra diff --git a/test/jdk/java/lang/reflect/records/RecordReflectionTest.java b/test/jdk/java/lang/reflect/records/RecordReflectionTest.java index 0598e88f0c1..5cc2fd4f8eb 100644 --- a/test/jdk/java/lang/reflect/records/RecordReflectionTest.java +++ b/test/jdk/java/lang/reflect/records/RecordReflectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -27,16 +27,17 @@ * @summary reflection test for records * @build R10 * @compile RecordReflectionTest.java - * @run testng/othervm RecordReflectionTest + * @run junit/othervm RecordReflectionTest */ import java.lang.annotation.*; import java.lang.reflect.*; import java.util.List; -import org.testng.annotations.*; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; -@Test public class RecordReflectionTest { class NoRecord {} @@ -78,8 +79,7 @@ public class RecordReflectionTest { R13 {} // compact constructor, will contain mandated parameters } - @DataProvider(name = "recordClasses") - public Object[][] recordClassData() { + public static Object[][] recordClassData() { return List.of(R1.class, R2.class, R3.class, @@ -96,17 +96,17 @@ public class RecordReflectionTest { ).stream().map(c -> new Object[] {c}).toArray(Object[][]::new); } - @Test(dataProvider = "recordClasses") + @ParameterizedTest + @MethodSource("recordClassData") public void testIsRecord(Class cls) { String message = cls.toGenericString(); assertTrue(cls.isRecord()); - assertTrue(cls.getSuperclass() == java.lang.Record.class); - assertTrue(cls.getRecordComponents() != null); + assertSame(Record.class, cls.getSuperclass()); + assertNotNull(cls.getRecordComponents()); assertTrue(message.contains("record"), message); } - @DataProvider(name = "notRecordClasses") - public Object[][] notRecordClasses() { + public static List> notRecordClasses() { return List.of(NoRecord.class, NoRecord[].class, Record.class, // java.lang.Record is not itself a record class @@ -116,19 +116,18 @@ public class RecordReflectionTest { int.class, int[].class, long.class, - long[].class) - .stream().map(c -> new Object[] {c}).toArray(Object[][]::new); + long[].class); } - @Test(dataProvider = "notRecordClasses") + @ParameterizedTest + @MethodSource("notRecordClasses") public void testNotARecordClass(Class cls) { assertFalse(cls.isRecord()); - assertFalse(cls.getSuperclass() == java.lang.Record.class); - assertTrue(cls.getRecordComponents() == null); + assertNotSame(Record.class, cls.getSuperclass()); + assertNull(cls.getRecordComponents()); } - @DataProvider(name = "reflectionData") - public Object[][] reflectionData() { + public static Object[][] reflectionData() { return new Object[][] { new Object[] { new R1(), 0, @@ -181,7 +180,8 @@ public class RecordReflectionTest { }; } - @Test(dataProvider = "reflectionData") + @ParameterizedTest + @MethodSource("reflectionData") public void testRecordReflection(Object recordOb, int numberOfComponents, Object[] values, @@ -192,13 +192,13 @@ public class RecordReflectionTest { Class recordClass = recordOb.getClass(); assertTrue(recordClass.isRecord()); RecordComponent[] recordComponents = recordClass.getRecordComponents(); - assertEquals(recordComponents.length, numberOfComponents); + assertEquals(numberOfComponents, recordComponents.length); int i = 0; for (RecordComponent rc : recordComponents) { - assertEquals(rc.getName(), names[i]); - assertEquals(rc.getType(), rc.getAccessor().getReturnType()); - assertEquals(rc.getAccessor().invoke(recordOb), values[i]); - assertEquals(rc.getAccessor().getGenericReturnType().toString(), signatures[i], + assertEquals(names[i], rc.getName()); + assertEquals(rc.getAccessor().getReturnType(), rc.getType()); + assertEquals(values[i], rc.getAccessor().invoke(recordOb)); + assertEquals(signatures[i], rc.getAccessor().getGenericReturnType().toString(), String.format("signature of method \"%s\" different from expected signature \"%s\"", rc.getAccessor().getGenericReturnType(), signatures[i])); i++; @@ -207,7 +207,7 @@ public class RecordReflectionTest { var constructor = recordClass.getDeclaredConstructors()[0]; i = 0; for (var p: constructor.getParameters()) { - assertEquals(p.getParameterizedType().toString(), signatures[i], + assertEquals(signatures[i], p.getParameterizedType().toString(), String.format("signature of method \"%s\" different from expected signature \"%s\"", p.getType().toString(), signatures[i])); i++; @@ -215,7 +215,7 @@ public class RecordReflectionTest { // similar as above but testing another API i = 0; for (var p : constructor.getGenericParameterTypes()) { - assertEquals(p.toString(), signatures[i], + assertEquals(signatures[i], p.toString(), String.format("signature of method \"%s\" different from expected signature \"%s\"", p.toString(), signatures[i])); i++; @@ -228,16 +228,17 @@ public class RecordReflectionTest { record AnnotatedRec(@RCA int i) {} + @Test public void testDeclAnnotationsInRecordComp() throws Throwable { Class recordClass = AnnotatedRec.class; RecordComponent rc = recordClass.getRecordComponents()[0]; Annotation[] annos = rc.getAnnotations(); - assertEquals(annos.length, 1); - assertEquals(annos[0].toString(), "@RecordReflectionTest.RCA()"); + assertEquals(1, annos.length); + assertEquals("@RecordReflectionTest.RCA()", annos[0].toString()); Field f = recordClass.getDeclaredField("i"); - assertEquals(f.getAnnotations().length, 1); - assertEquals(f.getAnnotations()[0].toString(), annos[0].toString()); + assertEquals(1, f.getAnnotations().length); + assertEquals(annos[0].toString(), f.getAnnotations()[0].toString()); } @Retention(RetentionPolicy.RUNTIME) @@ -246,30 +247,28 @@ public class RecordReflectionTest { record TypeAnnotatedRec(@TYPE_USE int i) {} + @Test public void testTypeAnnotationsInRecordComp() throws Throwable { Class recordClass = TypeAnnotatedRec.class; RecordComponent rc = recordClass.getRecordComponents()[0]; AnnotatedType at = rc.getAnnotatedType(); Annotation[] annos = at.getAnnotations(); - assertEquals(annos.length, 1); - assertEquals(annos[0].toString(), "@RecordReflectionTest.TYPE_USE()"); + assertEquals(1, annos.length); + assertEquals("@RecordReflectionTest.TYPE_USE()", annos[0].toString()); Field f = recordClass.getDeclaredField("i"); - assertEquals(f.getAnnotatedType().getAnnotations().length, 1); - assertEquals(f.getAnnotatedType().getAnnotations()[0].toString(), annos[0].toString()); + assertEquals(1, f.getAnnotatedType().getAnnotations().length); + assertEquals(annos[0].toString(), f.getAnnotatedType().getAnnotations()[0].toString()); } + @Test public void testReadOnlyFieldInRecord() throws Throwable { R2 o = new R2(1, 2); Class recordClass = R2.class; String fieldName = "i"; Field f = recordClass.getDeclaredField(fieldName); assertTrue(f.trySetAccessible()); - assertTrue(f.get(o) != null); - try { - f.set(o, null); - assertTrue(false, "should fail to set " + fieldName); - } catch (IllegalAccessException e) { - } + assertNotNull(f.get(o)); + assertThrows(IllegalAccessException.class, () -> f.set(o, null)); } } diff --git a/test/jdk/java/lang/reflect/sealed_classes/SealedClassesReflectionTest.java b/test/jdk/java/lang/reflect/sealed_classes/SealedClassesReflectionTest.java index 03caebb49e8..6e14a5d540e 100644 --- a/test/jdk/java/lang/reflect/sealed_classes/SealedClassesReflectionTest.java +++ b/test/jdk/java/lang/reflect/sealed_classes/SealedClassesReflectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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,18 +26,17 @@ * @bug 8227046 * @summary reflection test for sealed classes * @compile SealedClassesReflectionTest.java - * @run testng/othervm SealedClassesReflectionTest + * @run junit/othervm SealedClassesReflectionTest */ import java.lang.annotation.*; -import java.lang.constant.ClassDesc; import java.lang.reflect.*; import java.util.Arrays; import java.util.List; -import org.testng.annotations.*; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; -@Test public class SealedClassesReflectionTest { sealed class SealedClass1 permits FinalSubclass1, NonSealedSubclass1 {} @@ -68,8 +67,7 @@ public class SealedClassesReflectionTest { sealed class SealedSubclass2 implements SealedInt3 permits NonSealedSubclass2 {} non-sealed class NonSealedSubclass2 extends SealedSubclass2 {} - @DataProvider(name = "sealedClasses") - public Object[][] sealedClassesData() { + public static List> sealedClassesData() { return List.of( SealedClass1.class, SealedClass2.class, @@ -79,19 +77,19 @@ public class SealedClassesReflectionTest { SealedInt2.class, SealedInt3.class, SealedSubInt1.class - ).stream().map(c -> new Object[] {c}).toArray(Object[][]::new); + ); } - @Test(dataProvider = "sealedClasses") + @ParameterizedTest + @MethodSource("sealedClassesData") public void testSealedClasses(Class cls) { assertTrue(cls.isSealed()); - assertTrue(!Modifier.isFinal(cls.getModifiers())); - assertTrue(cls.getPermittedSubclasses() != null); + assertFalse(Modifier.isFinal(cls.getModifiers())); + assertNotNull(cls.getPermittedSubclasses()); assertTrue(cls.getPermittedSubclasses().length > 0); } - @DataProvider(name = "notSealedClasses") - public Object[][] notSealedClassesData() { + public static List> notSealedClassesData() { return List.of( Object.class, void.class, Void.class, Void[].class, @@ -104,34 +102,34 @@ public class SealedClassesReflectionTest { double.class, double[].class, Double.class, Double[].class, boolean.class, boolean[].class, Boolean.class, Boolean[].class, String.class, String[].class - ).stream().map(c -> new Object[] {c}).toArray(Object[][]::new); + ); } - @Test(dataProvider = "notSealedClasses") + @ParameterizedTest + @MethodSource("notSealedClassesData") public void testNotSealedClasses(Class cls) { - assertTrue(!cls.isSealed()); - assertTrue(cls.getPermittedSubclasses() == null); + assertFalse(cls.isSealed()); + assertNull(cls.getPermittedSubclasses()); } - @DataProvider(name = "non_sealedClasses") - public Object[][] non_sealedClassesData() { + public static List> non_sealedClassesData() { return List.of( NonSealedSubclass1.class, NonSealedSubInt1.class, NonSealedSubclass2.class - ).stream().map(c -> new Object[] {c}).toArray(Object[][]::new); + ); } - @Test(dataProvider = "non_sealedClasses") + @ParameterizedTest + @MethodSource("non_sealedClassesData") public void testnon_sealedClasses(Class cls) { - assertTrue(!cls.isSealed()); - assertTrue(!Modifier.isFinal(cls.getModifiers())); + assertFalse(cls.isSealed()); + assertFalse(Modifier.isFinal(cls.getModifiers())); assertTrue((cls.getSuperclass() != null && cls.getSuperclass().isSealed()) || Arrays.stream(cls.getInterfaces()).anyMatch(Class::isSealed)); - assertTrue(cls.getPermittedSubclasses() == null); + assertNull(cls.getPermittedSubclasses()); } - @DataProvider(name = "reflectionData") - public Object[][] reflectionData() { + public static Object[][] reflectionData() { return new Object[][] { new Object[] { SealedClass1.class, @@ -204,7 +202,8 @@ public class SealedClassesReflectionTest { SEALED, NON_SEALED, FINAL } - @Test(dataProvider = "reflectionData") + @ParameterizedTest + @MethodSource("reflectionData") public void testSealedReflection(Class sealedClass, int numberOfSubclasses, String[] subclassDescriptors, @@ -213,10 +212,10 @@ public class SealedClassesReflectionTest { throws ReflectiveOperationException { assertTrue(sealedClass.isSealed()); - assertTrue(sealedClass.getPermittedSubclasses().length == numberOfSubclasses); + assertEquals(numberOfSubclasses, sealedClass.getPermittedSubclasses().length); int i = 0; for (Class cd : sealedClass.getPermittedSubclasses()) { - assertTrue(cd.getName().equals(subclassDescriptors[i]), "expected: " + subclassDescriptors[i] + " found: " + cd.getName()); + assertEquals(subclassDescriptors[i], cd.getName()); i++; } i = 0; diff --git a/test/jdk/java/lang/runtime/ExactnessConversionsSupportTest.java b/test/jdk/java/lang/runtime/ExactnessConversionsSupportTest.java index 6e1ef8e4c9f..3fbdfc5e9ab 100644 --- a/test/jdk/java/lang/runtime/ExactnessConversionsSupportTest.java +++ b/test/jdk/java/lang/runtime/ExactnessConversionsSupportTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, 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 @@ -21,46 +21,20 @@ * questions. */ -import org.testng.annotations.Test; - -import java.io.Serializable; -import java.lang.Enum.EnumDesc; -import java.lang.classfile.ClassFile; -import java.lang.constant.ClassDesc; -import java.lang.constant.ConstantDescs; -import java.lang.constant.MethodTypeDesc; -import java.lang.invoke.CallSite; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.lang.reflect.AccessFlag; import java.lang.runtime.ExactConversionsSupport; -import java.lang.runtime.SwitchBootstraps; -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.testng.Assert.*; +import org.junit.jupiter.api.Test; /** * @test * @bug 8304487 * @summary Verify boundary and special cases of exact conversion predicates * @compile ExactnessConversionsSupportTest.java - * @run testng/othervm ExactnessConversionsSupportTest + * @run junit/othervm ExactnessConversionsSupportTest */ -@Test public class ExactnessConversionsSupportTest { - public static void main(String[] args) { - testByte(); - testShort(); - testChar(); - testInt(); - testLong(); - testFloat(); - testDouble(); - } - - public static void testByte() { + @Test + public void testByte() { assertEquals(true, ExactConversionsSupport.isIntToByteExact((byte) (Byte.MAX_VALUE))); assertEquals(true, ExactConversionsSupport.isIntToByteExact((byte) (0))); assertEquals(true, ExactConversionsSupport.isIntToByteExact((byte) (Byte.MIN_VALUE))); @@ -92,7 +66,8 @@ public class ExactnessConversionsSupportTest { assertEquals(false, ExactConversionsSupport.isDoubleToByteExact(-0.0d)); assertEquals(true, ExactConversionsSupport.isDoubleToByteExact(+0.0d)); } - public static void testShort() { + @Test + public void testShort() { assertEquals(true, ExactConversionsSupport.isIntToShortExact((byte) (Byte.MAX_VALUE))); assertEquals(true, ExactConversionsSupport.isIntToShortExact((byte) (0))); assertEquals(true, ExactConversionsSupport.isIntToShortExact((byte) (Byte.MIN_VALUE))); @@ -125,7 +100,8 @@ public class ExactnessConversionsSupportTest { assertEquals(false, ExactConversionsSupport.isDoubleToShortExact(-0.0d)); assertEquals(true, ExactConversionsSupport.isDoubleToShortExact(+0.0d)); } - public static void testChar() { + @Test + public void testChar() { assertEquals(true, ExactConversionsSupport.isIntToCharExact((byte) (Byte.MAX_VALUE))); assertEquals(true, ExactConversionsSupport.isIntToCharExact((byte) (0))); assertEquals(false, ExactConversionsSupport.isIntToCharExact((byte) (Byte.MIN_VALUE))); @@ -157,7 +133,8 @@ public class ExactnessConversionsSupportTest { assertEquals(false, ExactConversionsSupport.isDoubleToCharExact(-0.0d)); assertEquals(true, ExactConversionsSupport.isDoubleToCharExact(+0.0d)); } - public static void testInt() { + @Test + public void testInt() { assertEquals(false, ExactConversionsSupport.isLongToIntExact((Long.MAX_VALUE))); assertEquals(true, ExactConversionsSupport.isLongToIntExact((0L))); assertEquals(false, ExactConversionsSupport.isLongToIntExact((Long.MIN_VALUE))); @@ -178,7 +155,8 @@ public class ExactnessConversionsSupportTest { assertEquals(false, ExactConversionsSupport.isDoubleToIntExact((-0.0d))); assertEquals(true, ExactConversionsSupport.isDoubleToIntExact((+0.0d))); } - public static void testLong() { + @Test + public void testLong() { assertEquals(false, ExactConversionsSupport.isFloatToLongExact((Float.MAX_VALUE))); assertEquals(true, ExactConversionsSupport.isFloatToLongExact(((float) 0))); assertEquals(false, ExactConversionsSupport.isFloatToLongExact((Float.MIN_VALUE))); @@ -196,7 +174,8 @@ public class ExactnessConversionsSupportTest { assertEquals(false, ExactConversionsSupport.isDoubleToLongExact((-0.0d))); assertEquals(true, ExactConversionsSupport.isDoubleToLongExact((+0.0d))); } - public static void testFloat() { + @Test + public void testFloat() { assertEquals(true, ExactConversionsSupport.isIntToFloatExact(((byte) (Byte.MAX_VALUE)))); assertEquals(true, ExactConversionsSupport.isIntToFloatExact(((byte) (0)))); assertEquals(true, ExactConversionsSupport.isIntToFloatExact(((byte) (Byte.MIN_VALUE)))); @@ -220,7 +199,8 @@ public class ExactnessConversionsSupportTest { assertEquals(true, ExactConversionsSupport.isDoubleToFloatExact((-0.0d))); assertEquals(true, ExactConversionsSupport.isDoubleToFloatExact((+0.0d))); } - public static void testDouble() { + @Test + public void testDouble() { assertEquals(false, ExactConversionsSupport.isLongToDoubleExact((Long.MAX_VALUE))); assertEquals(true, ExactConversionsSupport.isLongToDoubleExact((0L))); assertEquals(true, ExactConversionsSupport.isLongToDoubleExact((Long.MIN_VALUE))); diff --git a/test/jdk/java/lang/runtime/ObjectMethodsTest.java b/test/jdk/java/lang/runtime/ObjectMethodsTest.java index 62c8b034ed1..951d3b68383 100644 --- a/test/jdk/java/lang/runtime/ObjectMethodsTest.java +++ b/test/jdk/java/lang/runtime/ObjectMethodsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -25,7 +25,7 @@ * @test * @bug 8246774 * @summary Basic tests for ObjectMethods - * @run testng ObjectMethodsTest + * @run junit ObjectMethodsTest */ import java.util.List; @@ -34,14 +34,14 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.runtime.ObjectMethods; -import org.testng.annotations.Test; import static java.lang.invoke.MethodType.methodType; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertThrows; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -@Test +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; + public class ObjectMethodsTest { public static class C { @@ -80,6 +80,7 @@ public class ObjectMethodsTest { static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + @Test public void testEqualsC() throws Throwable { CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "equals", C.EQUALS_DESC, C.class, C.NAME_LIST, C.ACCESSORS); MethodHandle handle = cs.dynamicInvoker(); @@ -92,6 +93,7 @@ public class ObjectMethodsTest { assertFalse((boolean)handle.invokeExact(c, new Object())); } + @Test public void testEqualsEmpty() throws Throwable { CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "equals", Empty.EQUALS_DESC, Empty.class, Empty.NAME_LIST, Empty.ACCESSORS); MethodHandle handle = cs.dynamicInvoker(); @@ -102,45 +104,50 @@ public class ObjectMethodsTest { assertFalse((boolean)handle.invokeExact(e, new Object())); } + @Test public void testHashCodeC() throws Throwable { CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "hashCode", C.HASHCODE_DESC, C.class, "x;y", C.ACCESSORS); MethodHandle handle = cs.dynamicInvoker(); C c = new C(6, 7); int hc = (int)handle.invokeExact(c); - assertEquals(hc, hashCombiner(c.x(), c.y())); + assertEquals(hashCombiner(c.x(), c.y()), hc); - assertEquals((int)handle.invokeExact(new C(100, 1)), hashCombiner(100, 1)); - assertEquals((int)handle.invokeExact(new C(0, 0)), hashCombiner(0, 0)); - assertEquals((int)handle.invokeExact(new C(-1, 100)), hashCombiner(-1, 100)); - assertEquals((int)handle.invokeExact(new C(100, 1)), hashCombiner(100, 1)); - assertEquals((int)handle.invokeExact(new C(100, -1)), hashCombiner(100, -1)); + assertEquals(hashCombiner(100, 1), (int)handle.invokeExact(new C(100, 1))); + assertEquals(hashCombiner(0, 0), (int)handle.invokeExact(new C(0, 0))); + assertEquals(hashCombiner(-1, 100), (int)handle.invokeExact(new C(-1, 100))); + assertEquals(hashCombiner(100, 1), (int)handle.invokeExact(new C(100, 1))); + assertEquals(hashCombiner(100, -1), (int)handle.invokeExact(new C(100, -1))); } + @Test public void testHashCodeEmpty() throws Throwable { CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "hashCode", Empty.HASHCODE_DESC, Empty.class, "", Empty.ACCESSORS); MethodHandle handle = cs.dynamicInvoker(); Empty e = new Empty(); - assertEquals((int)handle.invokeExact(e), 0); + assertEquals(0, (int)handle.invokeExact(e)); } + @Test public void testToStringC() throws Throwable { CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "toString", C.TO_STRING_DESC, C.class, C.NAME_LIST, C.ACCESSORS); MethodHandle handle = cs.dynamicInvoker(); - assertEquals((String)handle.invokeExact(new C(8, 9)), "C[x=8, y=9]" ); - assertEquals((String)handle.invokeExact(new C(10, 11)), "C[x=10, y=11]" ); - assertEquals((String)handle.invokeExact(new C(100, -9)), "C[x=100, y=-9]"); - assertEquals((String)handle.invokeExact(new C(0, 0)), "C[x=0, y=0]" ); + assertEquals("C[x=8, y=9]", (String)handle.invokeExact(new C(8, 9)) ); + assertEquals("C[x=10, y=11]", (String)handle.invokeExact(new C(10, 11)) ); + assertEquals("C[x=100, y=-9]", (String)handle.invokeExact(new C(100, -9))); + assertEquals("C[x=0, y=0]", (String)handle.invokeExact(new C(0, 0)) ); } + @Test public void testToStringEmpty() throws Throwable { CallSite cs = (CallSite)ObjectMethods.bootstrap(LOOKUP, "toString", Empty.TO_STRING_DESC, Empty.class, Empty.NAME_LIST, Empty.ACCESSORS); MethodHandle handle = cs.dynamicInvoker(); - assertEquals((String)handle.invokeExact(new Empty()), "Empty[]"); + assertEquals("Empty[]", (String)handle.invokeExact(new Empty())); } Class NPE = NullPointerException.class; Class IAE = IllegalArgumentException.class; + @Test public void exceptions() { assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "badName", C.EQUALS_DESC, C.class, C.NAME_LIST, C.ACCESSORS)); assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, "toString", C.TO_STRING_DESC, C.class, "x;y;z", C.ACCESSORS)); diff --git a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java index 3a8608866dc..061ce2ae241 100644 --- a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java +++ b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java @@ -35,21 +35,20 @@ import java.lang.runtime.SwitchBootstraps; import java.util.concurrent.atomic.AtomicBoolean; import java.lang.classfile.ClassFile; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @test * @bug 8318144 * @enablePreview * @compile SwitchBootstrapsTest.java - * @run testng/othervm SwitchBootstrapsTest + * @run junit/othervm SwitchBootstrapsTest */ -@Test public class SwitchBootstrapsTest { public static final MethodHandle BSM_TYPE_SWITCH; @@ -70,14 +69,14 @@ public class SwitchBootstrapsTest { private void testType(Object target, int start, int result, Object... labels) throws Throwable { MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker(); - assertEquals((int) indy.invoke(target, start), result); + assertEquals(result, (int) indy.invoke(target, start)); assertEquals(-1, (int) indy.invoke(null, start)); } private void testPrimitiveType(Object target, Class targetType, int start, int result, Object... labels) throws Throwable { MethodType switchType = MethodType.methodType(int.class, targetType, int.class); MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker(); - assertEquals((int) indy.invoke(target, start), result); + assertEquals(result, (int) indy.invoke(target, start)); } private void testEnum(Enum target, int start, int result, Object... labels) throws Throwable { @@ -87,7 +86,7 @@ public class SwitchBootstrapsTest { private void testEnum(Class targetClass, Enum target, int start, int result, Object... labels) throws Throwable { MethodType switchType = MethodType.methodType(int.class, targetClass, int.class); MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker(); - assertEquals((int) indy.invoke(target, start), result); + assertEquals(result, (int) indy.invoke(target, start)); assertEquals(-1, (int) indy.invoke(null, start)); } @@ -100,6 +99,7 @@ public class SwitchBootstrapsTest { C; } + @Test public void testTypes() throws Throwable { testType("", 0, 0, String.class, Object.class); testType("", 0, 0, Object.class); @@ -138,6 +138,7 @@ public class SwitchBootstrapsTest { }, 0, 1, 1L); } + @Test public void testPrimitiveTypes() throws Throwable { testPrimitiveType((short) 1, short.class, 0, 1, String.class); testPrimitiveType((byte) 1, byte.class,0, 1, String.class, byte.class); @@ -150,52 +151,32 @@ public class SwitchBootstrapsTest { testPrimitiveType(true, boolean.class,0, 1, String.class, boolean.class); } + @Test public void testEnums() throws Throwable { testEnum(E1.A, 0, 2, "B", "C", "A", E1.class); testEnum(E1.B, 0, 0, "B", "C", "A", E1.class); testEnum(E1.B, 1, 3, "B", "C", "A", E1.class); - try { - testEnum(E1.B, 0, -1, E2.class); - fail("Didn't get the expected exception."); - } catch (IllegalArgumentException ex) { - //OK - } - try { - testEnum(E1.B, 0, -1, String.class); - fail("Didn't get the expected exception."); - } catch (IllegalArgumentException ex) { - //OK - } - try { - testEnum(E1.B, 0, -1, 10); - fail("Didn't get the expected exception."); - } catch (IllegalArgumentException ex) { - //OK - } - try { - testEnum(E1.B, 0, -1, new Object()); - fail("Didn't get the expected exception."); - } catch (IllegalArgumentException ex) { - //OK - } - try { - testEnum(E1.B, 0, -1, new Object[] { null }); - fail("Didn't get the expected exception."); - } catch (IllegalArgumentException ex) { - //OK - } - try { - testEnum(E1.B, 0, -1, ""); - fail("Didn't get the expected exception."); - } catch (IllegalArgumentException ex) { - //OK - } - try { - testEnum(E1.B, 0, -1, (Object[]) null); - fail("Didn't get the expected exception."); - } catch (NullPointerException ex) { - //OK - } + assertThrows(IllegalArgumentException.class, () -> + testEnum(E1.B, 0, -1, E2.class) + ); + assertThrows(IllegalArgumentException.class, () -> + testEnum(E1.B, 0, -1, String.class) + ); + assertThrows(IllegalArgumentException.class, () -> + testEnum(E1.B, 0, -1, 10) + ); + assertThrows(IllegalArgumentException.class, () -> + testEnum(E1.B, 0, -1, new Object()) + ); + assertThrows(IllegalArgumentException.class, () -> + testEnum(E1.B, 0, -1, new Object[] { null }) + ); + assertThrows(IllegalArgumentException.class, () -> + testEnum(E1.B, 0, -1, "") + ); + assertThrows(NullPointerException.class, () -> + testEnum(E1.B, 0, -1, (Object[]) null) + ); testEnum(E1.B, 0, 0, "B", "A"); testEnum(E1.A, 0, 1, "B", "A"); testEnum(E1.A, 0, 0, "A", "A", "B"); @@ -208,9 +189,10 @@ public class SwitchBootstrapsTest { //null invocation name: MethodType switchType = MethodType.methodType(int.class, E1.class, int.class); MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), null, switchType)).dynamicInvoker(); - assertEquals((int) indy.invoke(E1.A, 0), 0); + assertEquals(0, (int) indy.invoke(E1.A, 0)); } + @Test public void testEnumsWithConstants() throws Throwable { enum E { A {}, @@ -239,18 +221,16 @@ public class SwitchBootstrapsTest { testEnum(E.class, E.C, 2, 2, "A", "B"); } + @Test public void testWrongSwitchTypes() throws Throwable { MethodType[] switchTypes = new MethodType[] { MethodType.methodType(int.class, Object.class), MethodType.methodType(int.class, Object.class, Integer.class) }; for (MethodType switchType : switchTypes) { - try { - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType); - fail("Didn't get the expected exception."); - } catch (IllegalArgumentException ex) { - //OK, expected - } + assertThrows(IllegalArgumentException.class, () -> + BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType) + ); } MethodType[] enumSwitchTypes = new MethodType[] { MethodType.methodType(int.class, Enum.class), @@ -259,25 +239,21 @@ public class SwitchBootstrapsTest { MethodType.methodType(int.class, Enum.class, Integer.class) }; for (MethodType enumSwitchType : enumSwitchTypes) { - try { - BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType); - fail("Didn't get the expected exception."); - } catch (IllegalArgumentException ex) { - //OK, expected - } + assertThrows(IllegalArgumentException.class, () -> + BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType) + ); } } + @Test public void testSwitchLabelTypes() throws Throwable { enum E {A} - try { - testType(E.A, 0, -1, E.A); - fail("Didn't get the expected exception."); - } catch (IllegalArgumentException ex) { - //OK, expected - } + assertThrows(IllegalArgumentException.class, () -> + testType(E.A, 0, -1, E.A) + ); } + @Test public void testSwitchQualifiedEnum() throws Throwable { enum E {A, B, C} Object[] labels = new Object[] { @@ -290,41 +266,31 @@ public class SwitchBootstrapsTest { testType(E.C, 0, 2, labels); } + @Test public void testNullLabels() throws Throwable { MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); - try { - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, (Object[]) null); - fail("Didn't get the expected exception."); - } catch (NullPointerException ex) { - //OK - } - try { + assertThrows(NullPointerException.class, () -> + BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, (Object[]) null) + ); + assertThrows(IllegalArgumentException.class, () -> BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, - new Object[] {1, null, String.class}); - fail("Didn't get the expected exception."); - } catch (IllegalArgumentException ex) { - //OK - } + new Object[] {1, null, String.class}) + ); MethodType enumSwitchType = MethodType.methodType(int.class, E1.class, int.class); - try { - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, (Object[]) null); - fail("Didn't get the expected exception."); - } catch (NullPointerException ex) { - //OK - } - try { + assertThrows(NullPointerException.class, () -> + BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, (Object[]) null) + ); + assertThrows(IllegalArgumentException.class, () -> BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, - new Object[] {1, null, String.class}); - fail("Didn't get the expected exception."); - } catch (IllegalArgumentException ex) { - //OK - } + new Object[] {1, null, String.class}) + ); //null invocationName is OK: BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), null, switchType, new Object[] {Object.class}); } private static AtomicBoolean enumInitialized = new AtomicBoolean(); + @Test public void testEnumInitialization1() throws Throwable { enumInitialized.set(false); @@ -340,13 +306,14 @@ public class SwitchBootstrapsTest { CallSite invocation = (CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, new Object[] {"A"}); assertFalse(enumInitialized.get()); - assertEquals(invocation.dynamicInvoker().invoke(null, 0), -1); + assertEquals(-1, invocation.dynamicInvoker().invoke(null, 0)); assertFalse(enumInitialized.get()); E e = E.A; assertTrue(enumInitialized.get()); - assertEquals(invocation.dynamicInvoker().invoke(e, 0), 0); + assertEquals(0, invocation.dynamicInvoker().invoke(e, 0)); } + @Test public void testEnumInitialization2() throws Throwable { enumInitialized.set(false); @@ -365,60 +332,46 @@ public class SwitchBootstrapsTest { }; CallSite invocation = (CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels); assertFalse(enumInitialized.get()); - assertEquals(invocation.dynamicInvoker().invoke(null, 0), -1); + assertEquals(-1, invocation.dynamicInvoker().invoke(null, 0)); assertFalse(enumInitialized.get()); - assertEquals(invocation.dynamicInvoker().invoke("test", 0), 1); + assertEquals(1, invocation.dynamicInvoker().invoke("test", 0)); assertFalse(enumInitialized.get()); E e = E.A; assertTrue(enumInitialized.get()); - assertEquals(invocation.dynamicInvoker().invoke(e, 0), 0); + assertEquals(0, invocation.dynamicInvoker().invoke(e, 0)); } + @Test public void testIncorrectEnumLabels() throws Throwable { - try { - testEnum(E1.B, 0, -1, "B", 1); - fail("Didn't get the expected exception."); - } catch (IllegalArgumentException ex) { - //OK - } - try { - testEnum(E1.B, 0, -1, "B", null); - fail("Didn't get the expected exception."); - } catch (IllegalArgumentException ex) { - //OK - } + assertThrows(IllegalArgumentException.class, () -> + testEnum(E1.B, 0, -1, "B", 1) + ); + assertThrows(IllegalArgumentException.class, () -> + testEnum(E1.B, 0, -1, "B", null) + ); } + @Test public void testIncorrectEnumStartIndex() throws Throwable { - try { - testEnum(E1.B, -1, -1, "B"); - fail("Didn't get the expected exception."); - } catch (IndexOutOfBoundsException ex) { - //OK - } - try { - testEnum(E1.B, 2, -1, "B"); - fail("Didn't get the expected exception."); - } catch (IndexOutOfBoundsException ex) { - //OK - } + assertThrows(IndexOutOfBoundsException.class, () -> + testEnum(E1.B, -1, -1, "B") + ); //OK + assertThrows(IndexOutOfBoundsException.class, () -> + testEnum(E1.B, 2, -1, "B") + ); } + @Test public void testIncorrectTypeStartIndex() throws Throwable { - try { - testType("", -1, -1, ""); - fail("Didn't get the expected exception."); - } catch (IndexOutOfBoundsException ex) { - //OK - } - try { - testType("", 2, -1, ""); - fail("Didn't get the expected exception."); - } catch (IndexOutOfBoundsException ex) { - //OK - } + assertThrows(IndexOutOfBoundsException.class, () -> + testType("", -1, -1, "") + ); + assertThrows(IndexOutOfBoundsException.class, () -> + testType("", 2, -1, "") + ); } + @Test public void testHiddenClassAsCaseLabel() throws Throwable { MethodHandles.Lookup lookup = MethodHandles.lookup(); byte[] classBytes = createClass(); @@ -448,30 +401,22 @@ public class SwitchBootstrapsTest { }); } + @Test public void testNullLookup() throws Throwable { - try { + assertThrows(NullPointerException.class, () -> { MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); BSM_TYPE_SWITCH.invoke(null, "", switchType, Object.class); - fail("Didn't get the expected exception."); - } catch (NullPointerException ex) { - //OK - } + }); enum E {} - try { + assertThrows(NullPointerException.class, () -> { MethodType switchType = MethodType.methodType(int.class, E.class, int.class); BSM_ENUM_SWITCH.invoke(null, "", switchType, - new Object[] {}); - fail("Didn't get the expected exception."); - } catch (NullPointerException ex) { - //OK - } - try { + new Object[] {}); + }); + assertThrows(NullPointerException.class, () -> { MethodType switchType = MethodType.methodType(int.class, E.class, int.class); BSM_ENUM_SWITCH.invoke(null, "", switchType, - new Object[] {"A"}); - fail("Didn't get the expected exception."); - } catch (NullPointerException ex) { - //OK - } + new Object[] {"A"}); + }); } } diff --git a/test/jdk/java/net/httpclient/CancelRequestTest.java b/test/jdk/java/net/httpclient/CancelRequestTest.java index 86e06bb78f7..2a0ec19a0ed 100644 --- a/test/jdk/java/net/httpclient/CancelRequestTest.java +++ b/test/jdk/java/net/httpclient/CancelRequestTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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,6 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLHandshakeException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -65,6 +64,7 @@ import java.net.http.HttpResponse.BodyHandlers; import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.Random; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; @@ -87,11 +87,14 @@ import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotEquals; import static org.testng.Assert.assertTrue; public class CancelRequestTest implements HttpServerAdapters { private static final Random random = RandomFactory.getRandom(); + private static final ConcurrentHashMap latches + = new ConcurrentHashMap<>(); private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] @@ -117,6 +120,7 @@ public class CancelRequestTest implements HttpServerAdapters { static volatile boolean tasksFailed; static final AtomicLong serverCount = new AtomicLong(); static final AtomicLong clientCount = new AtomicLong(); + static final AtomicLong requestCount = new AtomicLong(); static final long start = System.nanoTime(); public static String now() { long now = System.nanoTime() - start; @@ -278,10 +282,12 @@ public class CancelRequestTest implements HttpServerAdapters { // set HTTP/3 version on the request when targeting // an HTTP/3 server private HttpRequest.Builder requestBuilder(String uri) { - var builder = HttpRequest.newBuilder(URI.create(uri)); + var u = URI.create(uri+"?req="+requestCount.incrementAndGet()); + var builder = HttpRequest.newBuilder(u); if (uri.contains("h3")) { builder.version(HTTP_3); } + builder.setHeader("X-expect-exception", "true"); return builder; } @@ -324,6 +330,24 @@ public class CancelRequestTest implements HttpServerAdapters { assertEquals(resp.statusCode(), 200); } + private static void releaseLatches() { + // release left over latches + for (var latch : latches.values()) { + latch.countDown(); + } + latches.clear(); + } + + private static CountDownLatch addLatchFor(HttpRequest req) { + // release left over latches + releaseLatches(); + String key = Objects.requireNonNull(req.uri().getRawQuery(), "query"); + var latch = new CountDownLatch(1); + latches.put(key, latch); + out.println(now() + "CountDownLatch " + latch + " added for " + req.uri()); + return latch; + } + @Test(dataProvider = "asyncurls") public void testGetSendAsync(String uri, boolean sameClient, boolean mayInterruptIfRunning) throws Exception { @@ -343,18 +367,21 @@ public class CancelRequestTest implements HttpServerAdapters { .GET() .setOption(H3_DISCOVERY, config) .build(); + var requestLatch = addLatchFor(req); + BodyHandler handler = BodyHandlers.ofString(); CountDownLatch latch = new CountDownLatch(1); + out.println(now() + "Sending (async): " + req.uri()); CompletableFuture> response = client.sendAsync(req, handler); var cf1 = response.whenComplete((r,t) -> System.out.println(t)); CompletableFuture> cf2 = cf1.whenComplete((r,t) -> latch.countDown()); - out.println("iteration: " + i + ", req: " + req.uri()); + out.println(now() + "iteration: " + i + ", req sent: " + req.uri()); out.println("response: " + response); out.println("cf1: " + cf1); out.println("cf2: " + cf2); delay(); cf1.cancel(mayInterruptIfRunning); - out.println("response after cancel: " + response); + out.println(now() + "response after cancel: " + response); out.println("cf1 after cancel: " + cf1); out.println("cf2 after cancel: " + cf2); try { @@ -362,8 +389,10 @@ public class CancelRequestTest implements HttpServerAdapters { assertEquals(body, String.join("", BODY.split("\\|"))); throw new AssertionError("Expected CancellationException not received"); } catch (ExecutionException x) { - out.println("Got expected exception: " + x); + out.println(now() + "Got expected exception: " + x); assertTrue(isCancelled(x)); + } finally { + requestLatch.countDown(); } // Cancelling the request may cause an IOException instead... @@ -371,7 +400,7 @@ public class CancelRequestTest implements HttpServerAdapters { try { cf1.get(); } catch (CancellationException | ExecutionException x) { - out.println("Got expected exception: " + x); + out.println(now() + "Got expected exception: " + x); assertTrue(isCancelled(x)); hasCancellationException = x instanceof CancellationException; } @@ -393,7 +422,7 @@ public class CancelRequestTest implements HttpServerAdapters { if (mayInterruptIfRunning) { if (CancellationException.class.isAssignableFrom(wrapped.getClass())) { cause = wrapped.getCause(); - out.println("CancellationException cause: " + x); + out.println(now() + "CancellationException cause: " + x); } else if (!isCancelled(cause)) { throw new RuntimeException("Unexpected cause: " + cause); } @@ -403,15 +432,15 @@ public class CancelRequestTest implements HttpServerAdapters { } } if (!IOException.class.isInstance(cause)) { - out.println("Unexpected cause: " + cause.getClass()); + out.println(now() + "Unexpected cause: " + cause.getClass()); cause.printStackTrace(out); } assertTrue(IOException.class.isAssignableFrom(cause.getClass())); if (mayInterruptIfRunning) { - out.println("Got expected exception: " + wrapped); + out.println(now() + "Got expected exception: " + wrapped); out.println("\tcause: " + cause); } else { - out.println("Unexpected exception: " + wrapped); + out.println(now() + "Unexpected exception: " + wrapped); wrapped.printStackTrace(out); throw x; } @@ -453,13 +482,13 @@ public class CancelRequestTest implements HttpServerAdapters { @Override public Iterator iterator() { // this is dangerous - out.println("waiting for completion on: " + cancelFuture); + out.println(now() + "waiting for completion on: " + cancelFuture); boolean async = random.nextBoolean(); Runnable cancel = () -> { - out.println("Cancelling from " + Thread.currentThread()); + out.println(now() + "Cancelling from " + Thread.currentThread()); var cf1 = cancelFuture.join(); cf1.cancel(mayInterruptIfRunning); - out.println("cancelled " + cf1); + out.println(now() + "cancelled " + cf1); }; if (async) executor.execute(cancel); else cancel.run(); @@ -474,16 +503,20 @@ public class CancelRequestTest implements HttpServerAdapters { .POST(HttpRequest.BodyPublishers.ofByteArrays(iterable)) .setOption(H3_DISCOVERY, config) .build(); + var requestLatch = addLatchFor(req); + BodyHandler handler = BodyHandlers.ofString(); CountDownLatch latch = new CountDownLatch(1); + out.println(now() + "Sending (async): " + req.uri()); CompletableFuture> response = client.sendAsync(req, handler); - var cf1 = response.whenComplete((r,t) -> System.out.println(t)); + var cf1 = response.whenComplete((r,t) -> System.out.println(now() + t)); CompletableFuture> cf2 = cf1.whenComplete((r,t) -> latch.countDown()); + out.println(now() + "iteration: " + i + ", req sent: " + req.uri()); out.println("response: " + response); out.println("cf1: " + cf1); out.println("cf2: " + cf2); cancelFuture.complete(cf1); - out.println("response after cancel: " + response); + out.println(now() + "response after cancel: " + response); out.println("cf1 after cancel: " + cf1); out.println("cf2 after cancel: " + cf2); try { @@ -491,8 +524,10 @@ public class CancelRequestTest implements HttpServerAdapters { assertEquals(body, String.join("", BODY.split("\\|"))); throw new AssertionError("Expected CancellationException not received"); } catch (ExecutionException x) { - out.println("Got expected exception: " + x); + out.println(now() + "Got expected exception: " + x); assertTrue(isCancelled(x)); + } finally { + requestLatch.countDown(); } // Cancelling the request may cause an IOException instead... @@ -500,7 +535,7 @@ public class CancelRequestTest implements HttpServerAdapters { try { cf1.get(); } catch (CancellationException | ExecutionException x) { - out.println("Got expected exception: " + x); + out.println(now() + "Got expected exception: " + x); assertTrue(isCancelled(x)); hasCancellationException = x instanceof CancellationException; } @@ -521,7 +556,7 @@ public class CancelRequestTest implements HttpServerAdapters { Throwable cause = wrapped; if (CancellationException.class.isAssignableFrom(wrapped.getClass())) { cause = wrapped.getCause(); - out.println("CancellationException cause: " + x); + out.println(now() + "CancellationException cause: " + x); } else if (!isCancelled(cause)) { throw new RuntimeException("Unexpected cause: " + cause); } @@ -531,10 +566,10 @@ public class CancelRequestTest implements HttpServerAdapters { throw new RuntimeException("Unexpected timeout exception", cause); } if (mayInterruptIfRunning) { - out.println("Got expected exception: " + wrapped); + out.println(now() + "Got expected exception: " + wrapped); out.println("\tcause: " + cause); } else { - out.println("Unexpected exception: " + wrapped); + out.println(now() + "Unexpected exception: " + wrapped); wrapped.printStackTrace(out); throw x; } @@ -571,7 +606,7 @@ public class CancelRequestTest implements HttpServerAdapters { Thread main = Thread.currentThread(); CompletableFuture interruptingThread = new CompletableFuture<>(); - var uriStr = uri + "/post/req=" + i; + var uriStr = uri + "/post/i=" + i; Runnable interrupt = () -> { Thread current = Thread.currentThread(); out.printf("%s Interrupting main from: %s (%s)%n", now(), current, uriStr); @@ -593,39 +628,42 @@ public class CancelRequestTest implements HttpServerAdapters { .POST(HttpRequest.BodyPublishers.ofByteArrays(iterable)) .setOption(H3_DISCOVERY, config) .build(); + var requestLatch = addLatchFor(req); + String body = null; Exception failed = null; try { - out.println("Sending: " + uriStr); + out.println(now() + "Sending: " + req.uri()); body = client.send(req, BodyHandlers.ofString()).body(); } catch (Exception x) { failed = x; } - out.println(uriStr + ": got result or exception"); + requestLatch.countDown(); + out.println(now() + req.uri() + ": got result or exception"); if (failed instanceof InterruptedException) { - out.println(uriStr + ": Got expected exception: " + failed); + out.println(now() + req.uri() + ": Got expected exception: " + failed); } else if (failed instanceof IOException) { - out.println(uriStr + ": got IOException: " + failed); + out.println(now() + req.uri() + ": got IOException: " + failed); // that could be OK if the main thread was interrupted // from the main thread: the interrupted status could have // been caught by writing to the socket from the main // thread. if (interruptingThread.isDone() && interruptingThread.get() == main) { - out.println(uriStr + ": Accepting IOException: " + failed); + out.println(now() + req.uri() + ": Accepting IOException: " + failed); failed.printStackTrace(out); } else { - out.println(uriStr + ": unexpected exception: " + failed); + out.println(now() + req.uri() + ": unexpected exception: " + failed); throw failed; } } else if (failed != null) { - out.println(uriStr + ": unexpected exception: " + failed); + out.println(now() + req.uri() + ": unexpected exception: " + failed); throw failed; } else { assert failed == null; - out.println(uriStr + ": got body: " + body); + out.println(now() + req.uri() + ": got body: " + body); assertEquals(body, String.join("", BODY.split("\\|"))); } - out.println("next iteration"); + out.println(now() + "next iteration"); var error = TRACKER.check(tracker, 2000, (t) -> t.getOutstandingOperations() > 0 || t.getOutstandingSubscribers() > 0, @@ -700,7 +738,7 @@ public class CancelRequestTest implements HttpServerAdapters { } finally { if (fail != null) { if (sharedClientName != null) { - System.err.println("Shared client name is: " + sharedClientName); + System.err.println(now() + "Shared client name is: " + sharedClientName); } throw fail; } @@ -719,9 +757,9 @@ public class CancelRequestTest implements HttpServerAdapters { @Override public void handle(HttpTestExchange t) throws IOException { try { - out.println("HTTPSlowHandler received request to " + t.getRequestURI()); - System.err.println("HTTPSlowHandler received request to " + t.getRequestURI()); - + out.println(now() + "HTTPSlowHandler received request to " + t.getRequestURI()); + System.err.println(now() + "HTTPSlowHandler received request to " + t.getRequestURI()); + var requestLatch = latches.get(t.getRequestURI().getRawQuery()); boolean isThreadInterrupt = isThreadInterrupt(t); byte[] req; try (InputStream is = t.getRequestBody()) { @@ -729,7 +767,7 @@ public class CancelRequestTest implements HttpServerAdapters { } t.sendResponseHeaders(200, -1); // chunked/variable try (OutputStream os = t.getResponseBody()) { - // lets split the response in several chunks... + // let's split the response in several chunks... String msg = (req != null && req.length != 0) ? new String(req, UTF_8) : BODY; @@ -743,16 +781,29 @@ public class CancelRequestTest implements HttpServerAdapters { } catch (InterruptedException x) { // OK } - out.printf("Server wrote %d bytes%n", req.length); + out.printf(now() + "Server wrote %d bytes%n", req.length); + } + if (requestLatch != null) { + out.printf(now() + "Server awaiting latch %s for %s%n", + requestLatch, t.getRequestURI()); + try { + requestLatch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + out.printf(now() + " ...latch released%n"); } } + } catch (IOException io) { + out.println(now() + "HTTPSlowHandler: IOexception is not unexpected: " + io); + throw io; } catch (Throwable e) { - out.println("HTTPSlowHandler: unexpected exception: " + e); + out.println(now() + "HTTPSlowHandler: unexpected exception: " + e); e.printStackTrace(); throw e; } finally { - out.printf("HTTPSlowHandler reply sent: %s%n", t.getRequestURI()); - System.err.printf("HTTPSlowHandler reply sent: %s%n", t.getRequestURI()); + out.printf(now() + "HTTPSlowHandler reply sent: %s%n", t.getRequestURI()); + System.err.printf(now() + "HTTPSlowHandler reply sent: %s%n", t.getRequestURI()); } } } diff --git a/test/jdk/java/net/httpclient/StreamingBody.java b/test/jdk/java/net/httpclient/StreamingBody.java index c2ad19cf7b7..3fb4f29208e 100644 --- a/test/jdk/java/net/httpclient/StreamingBody.java +++ b/test/jdk/java/net/httpclient/StreamingBody.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -25,11 +25,12 @@ * @test * @summary Exercise a streaming subscriber ( InputStream ) without holding a * strong (or any ) reference to the client. + * @key randomness * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.httpclient.test.lib.common.HttpServerAdapters jdk.test.lib.net.SimpleSSLContext - * @run testng/othervm + * @run junit/othervm * -Djdk.httpclient.HttpClient.log=trace,headers,requests - * StreamingBody + * ${test.main.class} */ import java.io.IOException; @@ -40,13 +41,15 @@ import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLContext; import jdk.httpclient.test.lib.common.HttpServerAdapters; +import jdk.test.lib.RandomFactory; import jdk.test.lib.net.SimpleSSLContext; -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; @@ -55,8 +58,21 @@ import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY; import static java.net.http.HttpOption.H3_DISCOVERY; import static java.nio.charset.StandardCharsets.UTF_8; import static java.net.http.HttpClient.Builder.NO_PROXY; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.extension.TestWatcher; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class StreamingBody implements HttpServerAdapters { private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); @@ -71,10 +87,105 @@ public class StreamingBody implements HttpServerAdapters { String https2URI; String http3URI; + static final AtomicLong clientCount = new AtomicLong(); + static final AtomicLong serverCount = new AtomicLong(); + static final ConcurrentMap FAILURES = new ConcurrentHashMap<>(); + private static boolean stopAfterFirstFailure() { + return true; + } + + static final long start = System.nanoTime(); + public static String now() { + long now = System.nanoTime() - start; + long secs = now / 1000_000_000; + long mill = (now % 1000_000_000) / 1000_000; + long nan = now % 1000_000; + return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); + } + + final static class TestStopper implements TestWatcher, BeforeEachCallback { + final AtomicReference failed = new AtomicReference<>(); + TestStopper() { } + @Override + public void testFailed(ExtensionContext context, Throwable cause) { + if (stopAfterFirstFailure()) { + String msg = "Aborting due to: " + cause; + failed.compareAndSet(null, msg); + FAILURES.putIfAbsent(context.getDisplayName(), cause); + System.out.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n", + now(), context.getDisplayName(), cause); + System.err.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n", + now(), context.getDisplayName(), cause); + } + } + + @Override + public void beforeEach(ExtensionContext context) { + String msg = failed.get(); + Assumptions.assumeTrue(msg == null, msg); + } + } + + @RegisterExtension + static final TestStopper stopper = new TestStopper(); + + /// The GCTrigger triggers GC at random intervals to + /// help garbage collecting HttpClient intances. This test + /// wants to verify that HttpClient instances which are no + /// longer strongly referenced are not garbage collected + /// before pending HTTP requests are finished. The test + /// creates many client instances (up to 500) and relies + /// on the GC to collect them, since it does not want to + /// keep a strong reference, and therefore cannot not call + /// close(). This can put extra load on the machine since + /// we can't (and don't want to) control when the GC will + /// intervene. The purpose of this class is to trigger the + /// GC at random intervals to 1. help garbage collect client + /// instances earlier, thus reducing the load on the machine, + /// and 2. potentially trigger bugs if the client gets + /// inadvertently GC'ed before the request is finished + /// (which is the bug we're testing for here). + static class GCTrigger { + private final long gcinterval; + private final Thread runner; + private volatile boolean stop; + private final static Random RANDOM = RandomFactory.getRandom(); + + GCTrigger(long gcinterval) { + this.gcinterval = Math.clamp(gcinterval, 100, Long.MAX_VALUE/2); + runner = Thread.ofPlatform().daemon().unstarted(this::loop); + } + + private void loop() { + long min = gcinterval / 2; + long max = gcinterval + min; + while (!stop) { + try { + Thread.sleep(RANDOM.nextLong(min, max)); + } catch (InterruptedException x) { + stop = true; + break; + } + out.println(now() + "triggering gc"); + System.gc(); + } + } + + public void start() { + runner.start(); + } + + public void stop() throws InterruptedException { + stop = true; + runner.interrupt(); + runner.join(); + } + } + + static GCTrigger gcTrigger; static final String MESSAGE = "StreamingBody message body"; static final int ITERATIONS = 100; - @DataProvider(name = "positive") public Object[][] positive() { return new Object[][] { { http3URI, }, @@ -94,72 +205,118 @@ public class StreamingBody implements HttpServerAdapters { return builder; } - @Test(dataProvider = "positive") + @ParameterizedTest + @MethodSource("positive") void test(String uriString) throws Exception { - out.printf("%n---- starting (%s) ----%n", uriString); + out.printf("%n%s---- starting (%s) ----%n", now(), uriString); URI uri = URI.create(uriString); HttpRequest request = newRequestBuilder(uri).build(); for (int i=0; i< ITERATIONS; i++) { - out.println("iteration: " + i); - var builder = uriString.contains("/http3/") - ? newClientBuilderForH3() - : HttpClient.newBuilder(); - HttpResponse response = builder - .sslContext(sslContext) - .proxy(NO_PROXY) - .build() - .sendAsync(request, BodyHandlers.ofInputStream()) - .join(); + try { + out.println(now() + "iteration: " + i); + var builder = uriString.contains("/http3/") + ? newClientBuilderForH3() + : HttpClient.newBuilder(); + clientCount.incrementAndGet(); - String body = new String(response.body().readAllBytes(), UTF_8); - out.println("Got response: " + response); - out.println("Got body Path: " + body); + // we want to relinquish the reference to the HttpClient facade + // as soon as possible. We're using `ofInputStream()` because + // the HttpResponse will be returned almost immediately, before + // the response is read. Similarly we use sendAsync() because + // this will return a CompletableFuture and not wait for the + // request to complete within a method called on the client + // facade. + HttpResponse response = builder + .sslContext(sslContext) + .proxy(NO_PROXY) + .build() + .sendAsync(request, BodyHandlers.ofInputStream()) + .join(); - assertEquals(response.statusCode(), 200); - assertEquals(body, MESSAGE); + String body = new String(response.body().readAllBytes(), UTF_8); + out.println("Got response: " + response); + out.println("Got body Path: " + body); + + assertEquals(200, response.statusCode()); + assertEquals(MESSAGE, body); + } catch (Throwable t) { + String msg = "%stest(%s)[%s] failed: %s" + .formatted(now(), uriString, i, t); + out.println(msg); + throw new AssertionError(msg, t); + } } } // -- Infrastructure - @BeforeTest + @BeforeAll public void setup() throws Exception { httpTestServer = HttpTestServer.create(HTTP_1_1); httpTestServer.addHandler(new MessageHandler(), "/http1/streamingbody/"); httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/streamingbody/w"; + serverCount.incrementAndGet(); httpsTestServer = HttpTestServer.create(HTTP_1_1, sslContext); httpsTestServer.addHandler(new MessageHandler(),"/https1/streamingbody/"); httpsURI = "https://" + httpsTestServer.serverAuthority() + "/https1/streamingbody/x"; + serverCount.incrementAndGet(); http2TestServer = HttpTestServer.create(HTTP_2); http2TestServer.addHandler(new MessageHandler(), "/http2/streamingbody/"); http2URI = "http://" + http2TestServer.serverAuthority() + "/http2/streamingbody/y"; + serverCount.incrementAndGet(); https2TestServer = HttpTestServer.create(HTTP_2, sslContext); https2TestServer.addHandler(new MessageHandler(), "/https2/streamingbody/"); https2URI = "https://" + https2TestServer.serverAuthority() + "/https2/streamingbody/z"; + serverCount.incrementAndGet(); http3TestServer = HttpTestServer.create(HTTP_3_URI_ONLY, sslContext); http3TestServer.addHandler(new MessageHandler(), "/http3/streamingbody/"); http3URI = "https://" + http3TestServer.serverAuthority() + "/http3/streamingbody/z"; + serverCount.incrementAndGet(); + + gcTrigger = new GCTrigger(500); httpTestServer.start(); httpsTestServer.start(); http2TestServer.start(); https2TestServer.start(); http3TestServer.start(); + gcTrigger.start(); } - @AfterTest + @AfterAll public void teardown() throws Exception { - httpTestServer.stop(); - httpsTestServer.stop(); - http2TestServer.stop(); - https2TestServer.stop(); - http3TestServer.stop(); + try { + httpTestServer.stop(); + httpsTestServer.stop(); + http2TestServer.stop(); + https2TestServer.stop(); + http3TestServer.stop(); + gcTrigger.stop(); + } finally { + printFailedTests(); + } + } + + static final void printFailedTests() { + out.println("\n========================="); + try { + out.printf("%n%sCreated %s servers and %s clients%n", + now(), serverCount.get(), clientCount.get()); + if (FAILURES.isEmpty()) return; + out.println("Failed tests: "); + FAILURES.entrySet().forEach((e) -> { + out.printf("\t%s: %s%n", e.getKey(), e.getValue()); + e.getValue().printStackTrace(out); + }); + } finally { + out.println("\n=========================\n"); + } } static class MessageHandler implements HttpTestHandler { diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerExchange.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerExchange.java index c127abf3c0f..1c61f00d164 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerExchange.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerExchange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -175,7 +175,7 @@ public final class Http3ServerExchange implements Http2TestExchange { } serverStream.writer.reset(Http3Error.H3_INTERNAL_ERROR.code()); } - is.close(io); + is.resetStream(io); os.closeInternal(); close(); } diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerStreamImpl.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerStreamImpl.java index c5a8709346c..ebe92fa3bac 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerStreamImpl.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerStreamImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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,6 +32,7 @@ import java.util.Objects; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; @@ -264,7 +265,7 @@ final class Http3ServerStreamImpl { // nothing to do - let the response be sent to the client, but throw an // exception if `is` is used again. exchangeCF.thenApply(en -> { - en.is.close(new IOException("stopSendingRequested")); + en.is.resetStream(new IOException("stopSendingRequested")); return en; }); return; @@ -292,38 +293,37 @@ final class Http3ServerStreamImpl { } class RequestBodyInputStream extends InputStream { - volatile IOException error; - volatile boolean closed; + // Non-null if the stream is terminated. + // Points to an IOException on error, or Boolean.TRUE on EOF. + private final AtomicReference closeReason = new AtomicReference<>(); // uses an unbounded blocking queue in which the readrLoop // publishes the DataFrames payload... ByteBuffer current; - // Use lock to avoid pinned threads on the blocking queue - final ReentrantLock lock = new ReentrantLock(); ByteBuffer current() throws IOException { - lock.lock(); - try { - while (true) { - if (current != null && current.hasRemaining()) { - return current; - } - if (current == QuicStreamReader.EOF) return current; - try { - if (debug.on()) - debug.log("Taking buffer from queue"); - // Blocking call - current = requestBodyQueue.take(); - } catch (InterruptedException e) { - var io = new InterruptedIOException(); - Thread.currentThread().interrupt(); - io.initCause(e); - close(io); - var error = this.error; - if (error != null) throw error; + while (true) { + Object reason = closeReason.get(); + if (reason != null) { + if (reason == Boolean.TRUE) { + throw new IOException("Stream is closed"); + } else { + throw new IOException((IOException)reason); } } - } finally { - lock.unlock(); + if (current != null && (current.hasRemaining() || current == QuicStreamReader.EOF)) { + return current; + } + try { + if (debug.on()) + debug.log("Taking buffer from queue"); + // Blocking call + current = requestBodyQueue.take(); + } catch (InterruptedException e) { + var io = new InterruptedIOException(); + Thread.currentThread().interrupt(); + io.initCause(e); + throw io; + } } } @@ -331,9 +331,7 @@ final class Http3ServerStreamImpl { public int read() throws IOException { ByteBuffer buffer = current(); if (buffer == QuicStreamReader.EOF) { - var error = this.error; - if (error == null) return -1; - throw error; + return -1; } return buffer.get() & 0xFF; } @@ -345,11 +343,7 @@ final class Http3ServerStreamImpl { while (remaining > 0) { ByteBuffer buffer = current(); if (buffer == QuicStreamReader.EOF) { - if (len == remaining) { - var error = this.error; - if (error == null) return -1; - throw error; - } else return len - remaining; + return len == remaining ? -1 : len - remaining; } int count = Math.min(buffer.remaining(), remaining); buffer.get(b, off + (len - remaining), count); @@ -360,33 +354,20 @@ final class Http3ServerStreamImpl { @Override public void close() throws IOException { - lock.lock(); - try { - if (closed) return; - closed = true; - - } finally { - lock.unlock(); - } + if (closeReason.getAndSet(Boolean.TRUE) == Boolean.TRUE) return; if (debug.on()) debug.log("Closing request body input stream"); requestBodyQueue.add(QuicStreamReader.EOF); + stream.requestStopSending(Http3Error.H3_NO_ERROR.code()); } - void close(IOException io) { - lock.lock(); - try { - if (closed) return; - closed = true; - error = io; - } finally { - lock.unlock(); - } + void resetStream(IOException io) { + if (!closeReason.compareAndSet(null, io)) return; if (debug.on()) { debug.log("Closing request body input stream: " + io); } - requestBodyQueue.clear(); requestBodyQueue.add(QuicStreamReader.EOF); + stream.requestStopSending(Http3Error.H3_NO_ERROR.code()); } } diff --git a/test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java b/test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java new file mode 100644 index 00000000000..5e7b6395a66 --- /dev/null +++ b/test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2026, 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. + * + * 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. + */ + +import java.io.IOException; +import java.net.ConnectException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.time.Duration; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeFalse; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/* + * @test + * @bug 8376290 + * @summary Verify that when a SocketChannel is registered with a Selector + * with an interest in CONNECT operation, then SocketChannel.finishConnect() + * throws the correct exception message, if the connect() fails + * @run junit ${test.main.class} + */ +class ConnectionRefusedMessage { + + /* + * On a non-blocking SocketChannel, registered with a Selector, this test method + * attempts a SocketChannel.connect() against an address that is expected to return + * Connection refused. The test then calls SocketChannel.finishConnect() when the + * Selector makes available the ready key for this connect operation and expects + * that finishConnect() throws a ConnectException with the expected exception message. + */ + @Test + void testFinishConnect() throws Exception { + // find a suitable address against which the connect() attempt + // will result in a Connection refused exception + final InetSocketAddress destAddr = findSuitableRefusedAddress(); + // skip the test if we couldn't find a port which would raise a connection refused error + assumeTrue(destAddr != null, + "couldn't find a suitable port which will generate a connection refused error"); + try (Selector selector = Selector.open(); + SocketChannel sc = SocketChannel.open()) { + + // non-blocking + sc.configureBlocking(false); + sc.register(selector, SelectionKey.OP_CONNECT); + + System.err.println("establishing connection to " + destAddr); + boolean connected = sc.connect(destAddr); + // this test checks the exception message of a ConnectException, so it's + // OK to skip the test if something unexpectedly accepted the connection + assumeFalse(connected, "unexpectedly connected to " + destAddr); + // wait for ready ops + int numReady = selector.select(Duration.ofMinutes(10).toMillis()); + System.err.println("Num ready keys = " + numReady); + for (SelectionKey readyKey : selector.selectedKeys()) { + System.err.println("ready key: " + readyKey); + assertTrue(readyKey.isConnectable(), "unexpected key, readyOps = " + + readyKey.readyOps()); + readyKey.cancel(); + try { + boolean success = sc.finishConnect(); + // this test checks the exception message of a ConnectException, so it's + // OK to skip the test if something unexpectedly accepted the connection + assumeFalse(success, "unexpectedly connected to " + destAddr); + // this test doesn't expect finishConnect() to return normally + // with a return value of false + fail("ConnectException was not thrown"); + } catch (ConnectException ce) { + System.err.println("got (expected) ConnectException - " + ce); + // verify exception message + if (!"Connection refused".equals(ce.getMessage())) { + // propagate the original exception + fail("unexpected exception message: " + ce.getMessage(), ce); + } + } + } + } + } + + // Try to find a suitable port to provoke a "Connection Refused" error. + private static InetSocketAddress findSuitableRefusedAddress() throws IOException { + final InetAddress loopbackAddr = InetAddress.getLoopbackAddress(); + // Ports 47, 51, 61 are in the IANA reserved port list, and + // are currently unassigned to any specific service. + // We use them here on the assumption that there won't be + // any service listening on them. + InetSocketAddress destAddr = new InetSocketAddress(loopbackAddr, 47); + try (SocketChannel sc1 = SocketChannel.open(destAddr)) { + // we managed to connect (unexpectedly), let's try the next reserved port + destAddr = new InetSocketAddress(loopbackAddr, 51); + try (SocketChannel sc2 = SocketChannel.open(destAddr)) { + } + // we managed to connect (unexpectedly again), let's try the next reserved port + // as a last attempt + destAddr = new InetSocketAddress(loopbackAddr, 61); + try (SocketChannel sc3 = SocketChannel.open(destAddr)) { + } + return null; + } catch (ConnectException x) { + } + // the address which will generate a connection refused, when a connection is attempted + return destAddr; + } +} diff --git a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java index 21204ba980a..a115d56c52f 100644 --- a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java +++ b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java @@ -21,31 +21,43 @@ * questions. */ -/* @test +/* @test id=tmp * @bug 4313887 6838333 8343020 8357425 * @summary Unit test for java.nio.file.SecureDirectoryStream * @requires (os.family == "linux" | os.family == "mac" | os.family == "aix") * @library .. /test/lib - * @build jdk.test.lib.Platform + * @build jdk.test.lib.Platform jtreg.SkippedException * @run main SecureDS */ +/* @test id=cwd + * @requires (os.family == "linux" | os.family == "mac" | os.family == "aix") + * @library .. /test/lib + * @build jdk.test.lib.Platform jtreg.SkippedException + * @run main SecureDS cwd + */ + import java.nio.file.*; import static java.nio.file.Files.*; import static java.nio.file.StandardOpenOption.*; import static java.nio.file.LinkOption.*; import java.nio.file.attribute.*; -import java.nio.channels.*; import java.io.IOException; import java.util.*; import jdk.test.lib.Platform; +import jtreg.SkippedException; public class SecureDS { static boolean supportsSymbolicLinks; public static void main(String[] args) throws IOException { - Path dir = TestUtil.createTemporaryDirectory(); + Path dir; + if (args.length > 0 && args[0].equals("cwd")) { + dir = TestUtil.createTemporaryDirectory(System.getProperty("user.dir")); + } else { + dir = TestUtil.createTemporaryDirectory(); + } try { DirectoryStream stream = newDirectoryStream(dir); stream.close(); @@ -301,7 +313,17 @@ public class SecureDS { Files.writeString(filepath, TEXT); try (DirectoryStream ds = Files.newDirectoryStream(dir);) { if (ds instanceof SecureDirectoryStream sds) { - sds.move(file, null, file); + try { + sds.move(file, null, file); + } catch (AtomicMoveNotSupportedException e) { + if (Files.getFileStore(cwd).equals(Files.getFileStore(dir))) { + // re-throw if move between same volume + throw e; + } else { + throw new SkippedException( + "java.nio.file.AtomicMoveNotSupportedException"); + } + } if (!TEXT.equals(Files.readString(result))) throw new RuntimeException(result + " content incorrect"); } else { @@ -311,11 +333,10 @@ public class SecureDS { boolean fileDeleted = Files.deleteIfExists(filepath); if (!fileDeleted) Files.deleteIfExists(result); + // clean-up + delete(dir1); + delete(dir2); } - - // clean-up - delete(dir1); - delete(dir2); } // null and ClosedDirectoryStreamException diff --git a/test/jdk/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java b/test/jdk/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java index 4586c33521f..b729438812f 100644 --- a/test/jdk/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java +++ b/test/jdk/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, 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 @@ -24,9 +24,12 @@ /* @test * @bug 4313887 6838333 8273922 * @summary Unit test for java.nio.file.attribute.UserDefinedFileAttributeView + * (use -Dseed=X to set PRNG seed) * @library ../.. /test/lib * @key randomness * @build jdk.test.lib.Platform + * @build jdk.test.lib.RandomFactory + * @build jtreg.SkippedException * @run main Basic */ @@ -38,17 +41,20 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; -import static java.nio.file.LinkOption.*; -import java.nio.file.attribute.*; +import java.nio.file.attribute.UserDefinedFileAttributeView; import java.util.Arrays; import java.util.Map; import java.util.Random; + import jdk.test.lib.Platform; +import jdk.test.lib.RandomFactory; + +import jtreg.SkippedException; public class Basic { // Must be indeterministic - private static final Random rand = new Random(); + private static final Random rand = RandomFactory.getRandom(); private static final String ATTR_NAME = "mime_type"; private static final String ATTR_VALUE = "text/plain"; @@ -281,10 +287,8 @@ public class Basic { // create temporary directory to run tests Path dir = TestUtil.createTemporaryDirectory(); try { - if (!Files.getFileStore(dir).supportsFileAttributeView("user")) { - System.out.println("UserDefinedFileAttributeView not supported - skip test"); - return; - } + if (!Files.getFileStore(dir).supportsFileAttributeView("user")) + throw new SkippedException("UserDefinedFileAttributeView not supported"); // test access to user defined attributes of regular file Path file = dir.resolve("foo.html"); @@ -310,7 +314,7 @@ public class Basic { Path link = dir.resolve("link"); Files.createSymbolicLink(link, target); try { - test(link, NOFOLLOW_LINKS); + test(link, LinkOption.NOFOLLOW_LINKS); } catch (IOException x) { // access to attributes of sym link may not be supported } finally { diff --git a/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/LeaseCheckInterval.java b/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/LeaseCheckInterval.java index 3fa349fd671..e7c990ebdbd 100644 --- a/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/LeaseCheckInterval.java +++ b/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/LeaseCheckInterval.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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,10 +31,9 @@ * may be delayed longer than expected. While this is not a spec violation * (because there are no timeliness guarantees for any of these garbage * collection-related events), the user might expect that an unreferenced() - * invocation for an object whose last client has terminated abnorally + * invocation for an object whose last client has terminated abnormally * should occur on relatively the same time order as the lease value * granted. - * @author Peter Jones * * @library ../../../testlibrary * @modules java.rmi/sun.rmi.registry @@ -47,40 +46,39 @@ import java.rmi.Remote; import java.rmi.RemoteException; -import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; import java.rmi.server.Unreferenced; +import java.time.Duration; +import java.time.Instant; +import java.util.concurrent.CountDownLatch; public class LeaseCheckInterval implements Remote, Unreferenced { public static final String BINDING = "LeaseCheckInterval"; + // lease expiry time (milliseconds) private static final long LEASE_VALUE = 10000; - private static final long TIMEOUT = 20000; + // the maximum allowed duration between the lease expiration and + // the Unreferenced.unreferenced() callback method to be invoked + private static final Duration EXPECTED_MAX_DURATION = Duration.ofMinutes(1); - private Object lock = new Object(); - private boolean unreferencedInvoked = false; + // will be counted down when Unreferenced.unreferenced() is invoked + private static final CountDownLatch callbackInvocationLatch = new CountDownLatch(1); + @Override public void unreferenced() { - System.err.println("unreferenced() method invoked"); - synchronized (lock) { - unreferencedInvoked = true; - lock.notify(); - } + System.err.println("[" + Instant.now() + "] unreferenced() method invoked"); + callbackInvocationLatch.countDown(); } public static void main(String[] args) throws Exception { - - System.err.println("\nRegression test for bug 4285878\n"); - /* * Set the duration of leases granted to a very small value, so that * we can test if expirations are detected in a roughly comparable * time. */ - System.setProperty("java.rmi.dgc.leaseValue", - String.valueOf(LEASE_VALUE)); - + System.setProperty("java.rmi.dgc.leaseValue", String.valueOf(LEASE_VALUE)); + System.err.println("running test with java.rmi.dgc.leaseValue set to " + LEASE_VALUE); LeaseCheckInterval obj = new LeaseCheckInterval(); JavaVM jvm = null; @@ -90,37 +88,32 @@ public class LeaseCheckInterval implements Remote, Unreferenced { Registry localRegistry = TestLibrary.createRegistryOnEphemeralPort(); int registryPort = TestLibrary.getRegistryPort(localRegistry); - System.err.println("created local registry"); + System.err.println("created local registry on port " + registryPort); localRegistry.bind(BINDING, obj); System.err.println("bound remote object in local registry"); - synchronized (obj.lock) { - System.err.println("starting remote client VM..."); - jvm = new JavaVM("SelfTerminator", "-Drmi.registry.port=" + - registryPort, ""); - jvm.start(); - - System.err.println("waiting for unreferenced() callback..."); - obj.lock.wait(TIMEOUT); - - if (obj.unreferencedInvoked) { - System.err.println("TEST PASSED: " + - "unreferenced() invoked in timely fashion"); - } else { - throw new RuntimeException( - "TEST FAILED: unreferenced() not invoked after " + - ((double) TIMEOUT / 1000.0) + " seconds"); - } - } - - } catch (Exception e) { - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } else { - throw new RuntimeException( - "TEST FAILED: unexpected exception: " + e.toString()); + System.err.println("starting remote client VM..."); + jvm = new JavaVM("SelfTerminator", "-Drmi.registry.port=" + registryPort, ""); + // launch the self terminating java application which will lookup + // the bound object (thus creating a lease) and then terminate itself (thus + // creating the condition for a lease expiry). + jvm.start(); + final Instant startTime = Instant.now(); + System.err.println("waiting for SelfTerminator process to complete"); + final int exitCode = jvm.waitFor(); + if (exitCode != 0) { + throw new AssertionError("SelfTerminator process exited with" + + " a non-zero exit code: " + exitCode); } + System.err.println("SelfTerminator process completed in " + + Duration.between(startTime, Instant.now()) + + ", now waiting for Unreferenced.unreferenced() callback to be invoked"); + callbackInvocationLatch.await(); + final Instant waitEndedAt = Instant.now(); + final Duration waitDuration = assertWithinExpectedTimeLimit(waitEndedAt, startTime); + System.err.println("TEST PASSED: unreferenced() invoked in timely" + + " fashion (duration=" + waitDuration + ")"); } finally { if (jvm != null) { jvm.destroy(); @@ -135,4 +128,22 @@ public class LeaseCheckInterval implements Remote, Unreferenced { } } } + + /* + * Verifies that the duration between the lease expiration and the callback + * invocation is within the expected limit. Throws an exception if the wait + * duration is larger than expected limit, else returns the actual wait duration. + */ + private static Duration assertWithinExpectedTimeLimit(final Instant waitEndedAt, + final Instant terminationStartedAt) { + + final Duration waitDuration = Duration.between(terminationStartedAt, waitEndedAt); + System.out.println("wait completed in " + waitDuration); + if (waitDuration.compareTo(EXPECTED_MAX_DURATION) > 0) { + throw new RuntimeException("Took unexpectedly long (duration=" + + waitDuration + ") to invoke Unreferenced.unreferenced()," + + " expected max duration=" + EXPECTED_MAX_DURATION); + } + return waitDuration; + } } diff --git a/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/SelfTerminator.java b/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/SelfTerminator.java index 4875634dbb3..36ebbf05bf1 100644 --- a/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/SelfTerminator.java +++ b/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/SelfTerminator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2026, 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 @@ -21,26 +21,32 @@ * questions. */ -/* - * - */ - import java.rmi.Remote; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; +import java.time.Instant; public class SelfTerminator { - public static void main(String[] args) { + public static void main(String[] args) throws Exception { try { + log("main() invoked"); int registryPort = Integer.parseInt(System.getProperty("rmi.registry.port")); Registry registry = LocateRegistry.getRegistry("", registryPort); Remote stub = registry.lookup(LeaseCheckInterval.BINDING); + log("looked up binding, now terminating the process"); Runtime.getRuntime().halt(0); - } catch (Exception e) { - e.printStackTrace(); + } catch (Throwable t) { + log("failure: " + t); + t.printStackTrace(); + throw t; // propagate any failures and fail the process } } + + private static void log(final String message) { + final Instant now = Instant.now(); + System.err.println("[" + now + "] " + SelfTerminator.class.getName() + " - " + message); + } } diff --git a/test/jdk/java/sql/JavatimeTest.java b/test/jdk/java/sql/JavatimeTest.java deleted file mode 100644 index 89344d7cdc6..00000000000 --- a/test/jdk/java/sql/JavatimeTest.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2013, 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. - * - * 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. - */ - -/* - *@test - *@bug 8007520 - *@summary Test those bridge methods to/from java.time date/time classes - * @key randomness - */ - -import java.util.Random; -import java.sql.Date; -import java.sql.Time; -import java.sql.Timestamp; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; - -public class JavatimeTest { - - static final int NANOS_PER_SECOND = 1000000000; - - public static void main(String[] args) throws Throwable { - int N = 10000; - long t1970 = new java.util.Date(70, 0, 01).getTime(); - Random r = new Random(); - for (int i = 0; i < N; i++) { - int days = r.nextInt(50) * 365 + r.nextInt(365); - long secs = t1970 + days * 86400 + r.nextInt(86400); - int nanos = r.nextInt(NANOS_PER_SECOND); - int nanos_ms = nanos / 1000000 * 1000000; // millis precision - long millis = secs * 1000 + r.nextInt(1000); - - LocalDateTime ldt = LocalDateTime.ofEpochSecond(secs, nanos, ZoneOffset.UTC); - LocalDateTime ldt_ms = LocalDateTime.ofEpochSecond(secs, nanos_ms, ZoneOffset.UTC); - Instant inst = Instant.ofEpochSecond(secs, nanos); - Instant inst_ms = Instant.ofEpochSecond(secs, nanos_ms); - //System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - - /////////// Timestamp //////////////////////////////// - Timestamp ta = new Timestamp(millis); - ta.setNanos(nanos); - if (!isEqual(ta.toLocalDateTime(), ta)) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - print(ta.toLocalDateTime(), ta); - throw new RuntimeException("FAILED: j.s.ts -> ldt"); - } - if (!isEqual(ldt, Timestamp.valueOf(ldt))) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - print(ldt, Timestamp.valueOf(ldt)); - throw new RuntimeException("FAILED: ldt -> j.s.ts"); - } - Instant inst0 = ta.toInstant(); - if (ta.getTime() != inst0.toEpochMilli() || - ta.getNanos() != inst0.getNano() || - !ta.equals(Timestamp.from(inst0))) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - throw new RuntimeException("FAILED: j.s.ts -> instant -> j.s.ts"); - } - inst = Instant.ofEpochSecond(secs, nanos); - Timestamp ta0 = Timestamp.from(inst); - if (ta0.getTime() != inst.toEpochMilli() || - ta0.getNanos() != inst.getNano() || - !inst.equals(ta0.toInstant())) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - throw new RuntimeException("FAILED: instant -> timestamp -> instant"); - } - - ////////// java.sql.Date ///////////////////////////// - // j.s.d/t uses j.u.d.equals() !!!!!!!! - java.sql.Date jsd = new java.sql.Date(millis); - if (!isEqual(jsd.toLocalDate(), jsd)) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - print(jsd.toLocalDate(), jsd); - throw new RuntimeException("FAILED: j.s.d -> ld"); - } - LocalDate ld = ldt.toLocalDate(); - if (!isEqual(ld, java.sql.Date.valueOf(ld))) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - print(ld, java.sql.Date.valueOf(ld)); - throw new RuntimeException("FAILED: ld -> j.s.d"); - } - ////////// java.sql.Time ///////////////////////////// - java.sql.Time jst = new java.sql.Time(millis); - if (!isEqual(jst.toLocalTime(), jst)) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - print(jst.toLocalTime(), jst); - throw new RuntimeException("FAILED: j.s.t -> lt"); - } - // millis precision - LocalTime lt = ldt_ms.toLocalTime(); - if (!isEqual(lt, java.sql.Time.valueOf(lt))) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - print(lt, java.sql.Time.valueOf(lt)); - throw new RuntimeException("FAILED: lt -> j.s.t"); - } - } - System.out.println("Passed!"); - } - - private static boolean isEqual(LocalDateTime ldt, Timestamp ts) { - ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault()); - return zdt.getYear() == ts.getYear() + 1900 && - zdt.getMonthValue() == ts.getMonth() + 1 && - zdt.getDayOfMonth() == ts.getDate() && - zdt.getHour() == ts.getHours() && - zdt.getMinute() == ts.getMinutes() && - zdt.getSecond() == ts.getSeconds() && - zdt.getNano() == ts.getNanos(); - } - - private static void print(LocalDateTime ldt, Timestamp ts) { - ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault()); - System.out.printf("ldt:ts %d/%d, %d/%d, %d/%d, %d/%d, %d/%d, %d/%d, nano:[%d/%d]%n", - zdt.getYear(), ts.getYear() + 1900, - zdt.getMonthValue(), ts.getMonth() + 1, - zdt.getDayOfMonth(), ts.getDate(), - zdt.getHour(), ts.getHours(), - zdt.getMinute(), ts.getMinutes(), - zdt.getSecond(), ts.getSeconds(), - zdt.getNano(), ts.getNanos()); - } - - private static boolean isEqual(LocalDate ld, java.sql.Date d) { - return ld.getYear() == d.getYear() + 1900 && - ld.getMonthValue() == d.getMonth() + 1 && - ld.getDayOfMonth() == d.getDate(); - } - - private static void print(LocalDate ld, java.sql.Date d) { - System.out.printf("%d/%d, %d/%d, %d/%d%n", - ld.getYear(), d.getYear() + 1900, - ld.getMonthValue(), d.getMonth() + 1, - ld.getDayOfMonth(), d.getDate()); - } - - private static boolean isEqual(LocalTime lt, java.sql.Time t) { - return lt.getHour() == t.getHours() && - lt.getMinute() == t.getMinutes() && - lt.getSecond() == t.getSeconds(); - } - - private static void print(LocalTime lt, java.sql.Time t) { - System.out.printf("%d/%d, %d/%d, %d/%d%n", - lt.getHour(), t.getHours(), - lt.getMinute(), t.getMinutes(), - lt.getSecond(), t.getSeconds()); - } -} diff --git a/test/jdk/java/sql/test/TEST.properties b/test/jdk/java/sql/test/TEST.properties new file mode 100644 index 00000000000..97c9e7bbe18 --- /dev/null +++ b/test/jdk/java/sql/test/TEST.properties @@ -0,0 +1,3 @@ +# JDBC unit tests uses JUnit +JUnit.dirs= . +lib.dirs = /java/sql/util diff --git a/test/jdk/java/sql/testng/test/sql/BatchUpdateExceptionTests.java b/test/jdk/java/sql/test/sql/BatchUpdateExceptionTests.java similarity index 98% rename from test/jdk/java/sql/testng/test/sql/BatchUpdateExceptionTests.java rename to test/jdk/java/sql/test/sql/BatchUpdateExceptionTests.java index 2b1111b7526..2d010b50e41 100644 --- a/test/jdk/java/sql/testng/test/sql/BatchUpdateExceptionTests.java +++ b/test/jdk/java/sql/test/sql/BatchUpdateExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.io.ByteArrayInputStream; import java.io.File; @@ -28,8 +28,10 @@ import java.io.ObjectInputStream; import java.sql.BatchUpdateException; import java.sql.SQLException; import java.util.Arrays; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.SerializedBatchUpdateException; import util.BaseTest; diff --git a/test/jdk/java/sql/testng/test/sql/CallableStatementTests.java b/test/jdk/java/sql/test/sql/CallableStatementTests.java similarity index 62% rename from test/jdk/java/sql/testng/test/sql/CallableStatementTests.java rename to test/jdk/java/sql/test/sql/CallableStatementTests.java index 7a4fe15ecac..dc5c347ef50 100644 --- a/test/jdk/java/sql/testng/test/sql/CallableStatementTests.java +++ b/test/jdk/java/sql/test/sql/CallableStatementTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -20,28 +20,33 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; +import org.junit.jupiter.params.provider.ValueSource; import util.BaseTest; import util.StubConnection; import java.sql.CallableStatement; import java.sql.SQLException; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class CallableStatementTests extends BaseTest { private CallableStatement cstmt; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { cstmt = new StubConnection().prepareCall("{call SuperHero_Proc(?)}"); } - @AfterMethod + @AfterEach public void tearDownMethod() throws Exception { cstmt.close(); } @@ -50,80 +55,84 @@ public class CallableStatementTests extends BaseTest { * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedLiteralValues") public void test00(String s, String expected) throws SQLException { - assertEquals(cstmt.enquoteLiteral(s), expected); + assertEquals(expected, cstmt.enquoteLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test01() throws SQLException { - cstmt.enquoteLiteral(null); + assertThrows(NullPointerException.class, () -> cstmt.enquoteLiteral(null)); } /* * Validate that enquoteIdentifier returns the expected value */ - @Test(dataProvider = "validIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedIdentifierValues") public void test02(String s, boolean alwaysQuote, String expected) throws SQLException { - assertEquals(cstmt.enquoteIdentifier(s, alwaysQuote), expected); + assertEquals(expected, cstmt.enquoteIdentifier(s, alwaysQuote)); } /* * Validate that a SQLException is thrown for values that are not valid * for a SQL identifier */ - @Test(dataProvider = "invalidIdentifierValues", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidEnquotedIdentifierValues") public void test03(String s, boolean alwaysQuote) throws SQLException { - cstmt.enquoteIdentifier(s, alwaysQuote); + assertThrows(SQLException.class, () -> cstmt.enquoteIdentifier(s, alwaysQuote)); } /* * Validate a NullPointerException is thrown is the string passed to * enquoteIdentiifer is null */ - @Test(dataProvider = "trueFalse", - expectedExceptions = NullPointerException.class) + @ParameterizedTest(autoCloseArguments = false) + @ValueSource(booleans = {true, false}) public void test04(boolean alwaysQuote) throws SQLException { - cstmt.enquoteIdentifier(null, alwaysQuote); + assertThrows(NullPointerException.class, () -> cstmt.enquoteIdentifier(null, alwaysQuote)); } /* * Validate that isSimpleIdentifier returns the expected value */ - @Test(dataProvider = "simpleIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("simpleIdentifierValues") public void test05(String s, boolean expected) throws SQLException { - assertEquals(cstmt.isSimpleIdentifier(s), expected); + assertEquals(expected, cstmt.isSimpleIdentifier(s)); } /* * Validate a NullPointerException is thrown if the string passed to * isSimpleIdentifier is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test06() throws SQLException { - cstmt.isSimpleIdentifier(null); + assertThrows(NullPointerException.class, () -> cstmt.isSimpleIdentifier(null)); } /* * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedNCharLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedNCharLiteralValues") public void test07(String s, String expected) throws SQLException { - assertEquals(cstmt.enquoteNCharLiteral(s), expected); + assertEquals(expected, cstmt.enquoteNCharLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteNCharLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test08() throws SQLException { - cstmt.enquoteNCharLiteral(null); + assertThrows(NullPointerException.class, () -> cstmt.enquoteNCharLiteral(null)); } } diff --git a/test/jdk/java/sql/testng/test/sql/ConnectionTests.java b/test/jdk/java/sql/test/sql/ConnectionTests.java similarity index 63% rename from test/jdk/java/sql/testng/test/sql/ConnectionTests.java rename to test/jdk/java/sql/test/sql/ConnectionTests.java index f40c2784e4a..534b5d7d96a 100644 --- a/test/jdk/java/sql/testng/test/sql/ConnectionTests.java +++ b/test/jdk/java/sql/test/sql/ConnectionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -20,22 +20,25 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; +import org.junit.jupiter.params.provider.ValueSource; import util.BaseTest; import util.StubConnection; import java.sql.SQLException; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class ConnectionTests extends BaseTest { protected StubConnection conn; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { conn = new StubConnection(); } @@ -44,80 +47,84 @@ public class ConnectionTests extends BaseTest { * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedLiteralValues") public void test00(String s, String expected) throws SQLException { - assertEquals(conn.enquoteLiteral(s), expected); + assertEquals(expected, conn.enquoteLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test01() throws SQLException { - conn.enquoteLiteral(null); + assertThrows(NullPointerException.class, () -> conn.enquoteLiteral(null)); } /* * Validate that enquoteIdentifier returns the expected value */ - @Test(dataProvider = "validIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedIdentifierValues") public void test02(String s, boolean alwaysQuote, String expected) throws SQLException { - assertEquals(conn.enquoteIdentifier(s, alwaysQuote), expected); + assertEquals(expected, conn.enquoteIdentifier(s, alwaysQuote)); } /* * Validate that a SQLException is thrown for values that are not valid * for a SQL identifier */ - @Test(dataProvider = "invalidIdentifierValues", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidEnquotedIdentifierValues") public void test03(String s, boolean alwaysQuote) throws SQLException { - conn.enquoteIdentifier(s, alwaysQuote); + assertThrows(SQLException.class, () -> conn.enquoteIdentifier(s, alwaysQuote)); } /* * Validate a NullPointerException is thrown is the string passed to * enquoteIdentiifer is null */ - @Test(dataProvider = "trueFalse", - expectedExceptions = NullPointerException.class) + @ParameterizedTest(autoCloseArguments = false) + @ValueSource(booleans = {true, false}) public void test04(boolean alwaysQuote) throws SQLException { - conn.enquoteIdentifier(null, alwaysQuote); + assertThrows(NullPointerException.class, () -> conn.enquoteIdentifier(null, alwaysQuote)); } /* * Validate that isSimpleIdentifier returns the expected value */ - @Test(dataProvider = "simpleIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("simpleIdentifierValues") public void test05(String s, boolean expected) throws SQLException { - assertEquals(conn.isSimpleIdentifier(s), expected); + assertEquals(expected, conn.isSimpleIdentifier(s)); } /* * Validate a NullPointerException is thrown if the string passed to * isSimpleIdentifier is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test06() throws SQLException { - conn.isSimpleIdentifier(null); + assertThrows(NullPointerException.class, () -> conn.isSimpleIdentifier(null)); } /* * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedNCharLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedNCharLiteralValues") public void test07(String s, String expected) throws SQLException { - assertEquals(conn.enquoteNCharLiteral(s), expected); + assertEquals(expected, conn.enquoteNCharLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteNCharLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test08() throws SQLException { - conn.enquoteNCharLiteral(null); + assertThrows(NullPointerException.class, () -> conn.enquoteNCharLiteral(null)); } } diff --git a/test/jdk/java/sql/testng/test/sql/DataTruncationTests.java b/test/jdk/java/sql/test/sql/DataTruncationTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/DataTruncationTests.java rename to test/jdk/java/sql/test/sql/DataTruncationTests.java index fbf7eebe90a..9d221b4a037 100644 --- a/test/jdk/java/sql/testng/test/sql/DataTruncationTests.java +++ b/test/jdk/java/sql/test/sql/DataTruncationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,12 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.DataTruncation; import java.sql.SQLException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class DataTruncationTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/DateTests.java b/test/jdk/java/sql/test/sql/DateTests.java similarity index 80% rename from test/jdk/java/sql/testng/test/sql/DateTests.java rename to test/jdk/java/sql/test/sql/DateTests.java index ae6c276c4f6..0c66dd15c98 100644 --- a/test/jdk/java/sql/testng/test/sql/DateTests.java +++ b/test/jdk/java/sql/test/sql/DateTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,14 +20,18 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.Date; -import java.time.Instant; import java.time.LocalDate; -import static org.testng.Assert.*; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import util.BaseTest; public class DateTests extends BaseTest { @@ -35,17 +39,18 @@ public class DateTests extends BaseTest { /* * Validate an IllegalArgumentException is thrown for an invalid Date string */ - @Test(dataProvider = "invalidDateValues", - expectedExceptions = IllegalArgumentException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidDateValues") public void test(String d) throws Exception { - Date.valueOf(d); + assertThrows(IllegalArgumentException.class, () -> Date.valueOf(d)); } /* * Test that a date created from a date string is equal to the value * returned from toString() */ - @Test(dataProvider = "validDateValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validDateValues") public void test00(String d, String expectedD) { Date d1 = Date.valueOf(d); Date d2 = Date.valueOf(expectedD); @@ -207,20 +212,20 @@ public class DateTests extends BaseTest { /* * Validate an NPE occurs when a null LocalDate is passed to valueOf */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test15() throws Exception { LocalDate ld = null; - Date.valueOf(ld); + assertThrows(NullPointerException.class, () -> Date.valueOf(ld)); } /* * Validate an UnsupportedOperationException occurs when toInstant() is * called */ - @Test(expectedExceptions = UnsupportedOperationException.class) + @Test public void test16() throws Exception { Date d = Date.valueOf("1961-08-30"); - Instant instant = d.toInstant(); + assertThrows(UnsupportedOperationException.class, d::toInstant); } /* @@ -271,55 +276,55 @@ public class DateTests extends BaseTest { /* * Validate an IllegalArgumentException is thrown for calling getHours */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test21() throws Exception { Date d = Date.valueOf("1961-08-30"); - d.getHours(); + assertThrows(IllegalArgumentException.class, d::getHours); } /* * Validate an IllegalArgumentException is thrown for calling getMinutes */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test22() throws Exception { Date d = Date.valueOf("1961-08-30"); - d.getMinutes(); + assertThrows(IllegalArgumentException.class, d::getMinutes); } /* * Validate an IllegalArgumentException is thrown for calling getSeconds */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test23() throws Exception { Date d = Date.valueOf("1961-08-30"); - d.getSeconds(); + assertThrows(IllegalArgumentException.class, d::getSeconds); } /* * Validate an IllegalArgumentException is thrown for calling setHours */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test24() throws Exception { Date d = Date.valueOf("1961-08-30"); - d.setHours(8); + assertThrows(IllegalArgumentException.class, () -> d.setHours(8)); } /* * Validate an IllegalArgumentException is thrown for calling setMinutes */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test25() throws Exception { Date d = Date.valueOf("1961-08-30"); - d.setMinutes(0); + assertThrows(IllegalArgumentException.class, () -> d.setMinutes(0)); } /* * Validate an IllegalArgumentException is thrown for calling setSeconds */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test26() throws Exception { Date d = Date.valueOf("1961-08-30"); - d.setSeconds(0); + assertThrows(IllegalArgumentException.class, () -> d.setSeconds(0)); } /* @@ -327,32 +332,31 @@ public class DateTests extends BaseTest { * to validate that an IllegalArgumentException will be thrown from the * valueOf method */ - @DataProvider(name = "invalidDateValues") - private Object[][] invalidDateValues() { - return new Object[][]{ - {"20009-11-01"}, - {"09-11-01"}, - {"-11-01"}, - {"2009-111-01"}, - {"2009--01"}, - {"2009-13-01"}, - {"2009-11-011"}, - {"2009-11-"}, - {"2009-11-00"}, - {"2009-11-33"}, - {"--"}, - {""}, - {null}, - {"-"}, - {"2009"}, - {"2009-01"}, - {"---"}, - {"2009-13--1"}, - {"1900-1-0"}, - {"2009-01-01 10:50:01"}, - {"1996-12-10 12:26:19.1"}, - {"10:50:01"} - }; + private Stream invalidDateValues() { + return Stream.of( + "20009-11-01", + "09-11-01", + "-11-01", + "2009-111-01", + "2009--01", + "2009-13-01", + "2009-11-011", + "2009-11-", + "2009-11-00", + "2009-11-33", + "--", + "", + null, + "-", + "2009", + "2009-01", + "---", + "2009-13--1", + "1900-1-0", + "2009-01-01 10:50:01", + "1996-12-10 12:26:19.1", + "10:50:01" + ); } /* @@ -360,14 +364,12 @@ public class DateTests extends BaseTest { * to validate that an IllegalArgumentException will not be thrown from the * valueOf method and the corect value from toString() is returned */ - @DataProvider(name = "validDateValues") - private Object[][] validDateValues() { - return new Object[][]{ - {"2009-08-30", "2009-08-30"}, - {"2009-01-8", "2009-01-08"}, - {"2009-1-01", "2009-01-01"}, - {"2009-1-1", "2009-01-01"} - - }; + private Stream validDateValues() { + return Stream.of( + Arguments.of("2009-08-30", "2009-08-30"), + Arguments.of("2009-01-8", "2009-01-08"), + Arguments.of("2009-1-01", "2009-01-01"), + Arguments.of("2009-1-1", "2009-01-01") + ); } } diff --git a/test/jdk/java/sql/testng/test/sql/DriverManagerTests.java b/test/jdk/java/sql/test/sql/DriverManagerTests.java similarity index 84% rename from test/jdk/java/sql/testng/test/sql/DriverManagerTests.java rename to test/jdk/java/sql/test/sql/DriverManagerTests.java index 6b6e8386835..3797b625d21 100644 --- a/test/jdk/java/sql/testng/test/sql/DriverManagerTests.java +++ b/test/jdk/java/sql/test/sql/DriverManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.io.BufferedReader; import java.io.ByteArrayInputStream; @@ -39,12 +39,10 @@ import java.util.Collections; import java.util.Properties; import java.util.stream.Collectors; -import static org.testng.Assert.*; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.StubDriver; public class DriverManagerTests { @@ -58,23 +56,11 @@ public class DriverManagerTests { public DriverManagerTests() { } - @BeforeClass - public static void setUpClass() throws Exception { - } - - @AfterClass - public static void tearDownClass() throws Exception { - } - - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { removeAllDrivers(); } - @AfterMethod - public void tearDownMethod() throws Exception { - } - /** * Utility method to remove all registered drivers */ @@ -113,7 +99,7 @@ public class DriverManagerTests { int[] vals = {-1, 0, 5}; for (int val : vals) { DriverManager.setLoginTimeout(val); - assertEquals(val, DriverManager.getLoginTimeout()); + assertEquals(DriverManager.getLoginTimeout(), val); } } @@ -121,20 +107,22 @@ public class DriverManagerTests { * Validate that NullPointerException is thrown when null is passed to * registerDriver */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test1() throws Exception { Driver d = null; - DriverManager.registerDriver(d); + assertThrows(NullPointerException.class, + () -> DriverManager.registerDriver(d)); } /** * Validate that NullPointerException is thrown when null is passed to * registerDriver */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test2() throws Exception { Driver d = null; - DriverManager.registerDriver(d, null); + assertThrows(NullPointerException.class, () -> + DriverManager.registerDriver(d, null)); } /** @@ -150,68 +138,68 @@ public class DriverManagerTests { * Validate that SQLException is thrown when there is no Driver to service * the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test4() throws Exception { - DriverManager.getConnection(InvalidURL); + assertThrows(SQLException.class, () -> DriverManager.getConnection(InvalidURL)); } /** * Validate that SQLException is thrown when there is no Driver to service * the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test5() throws Exception { - DriverManager.getConnection(InvalidURL, new Properties()); + assertThrows(SQLException.class, () -> DriverManager.getConnection(InvalidURL, new Properties())); } /** * Validate that SQLException is thrown when there is no Driver to service * the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test6() throws Exception { - DriverManager.getConnection(InvalidURL, "LuckyDog", "tennisanyone"); + assertThrows(SQLException.class, () -> DriverManager.getConnection(InvalidURL, "LuckyDog", "tennisanyone")); } /** * Validate that SQLException is thrown when null is passed for the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test7() throws Exception { - DriverManager.getConnection(null); + assertThrows(SQLException.class, () -> DriverManager.getConnection(null)); } /** * Validate that SQLException is thrown when null is passed for the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test8() throws Exception { - DriverManager.getConnection(null, new Properties()); + assertThrows(SQLException.class, () -> DriverManager.getConnection(null, new Properties())); } /** * Validate that SQLException is thrown when null is passed for the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test9() throws Exception { - DriverManager.getConnection(null, "LuckyDog", "tennisanyone"); + assertThrows(SQLException.class, () -> DriverManager.getConnection(null, "LuckyDog", "tennisanyone")); } /** * Validate that SQLException is thrown when there is no Driver to service * the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test10() throws Exception { - DriverManager.getDriver(InvalidURL); + assertThrows(SQLException.class, () -> DriverManager.getDriver(InvalidURL)); } /** * Validate that SQLException is thrown when null is passed for the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test11() throws Exception { - DriverManager.getDriver(null); + assertThrows(SQLException.class, () -> DriverManager.getDriver(null)); } /** @@ -229,10 +217,10 @@ public class DriverManagerTests { * Validate that SQLException is thrown when the URL is not valid for any of * the registered drivers */ - @Test(expectedExceptions = SQLException.class) + @Test public void test13() throws Exception { DriverManager.registerDriver(new StubDriver()); - DriverManager.getDriver(InvalidURL); + assertThrows(SQLException.class, () -> DriverManager.getDriver(InvalidURL)); } /** @@ -370,9 +358,9 @@ public class DriverManagerTests { } Collection expectedDrivers = Collections.list(DriverManager.getDrivers()); - assertEquals(expectedDrivers.size(), n); + assertEquals(n, expectedDrivers.size()); Collection drivers = DriverManager.drivers().collect(Collectors.toList()); - assertEquals(drivers, expectedDrivers); + assertEquals(expectedDrivers, drivers); } } diff --git a/test/jdk/java/sql/test/sql/JavatimeTest.java b/test/jdk/java/sql/test/sql/JavatimeTest.java new file mode 100644 index 00000000000..acc0ac3e841 --- /dev/null +++ b/test/jdk/java/sql/test/sql/JavatimeTest.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2013, 2026, 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. + * + * 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 sql; + +/* + * @test + * @bug 8007520 + * @summary Test those bridge methods to/from java.time date/time classes + * @key randomness + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.FieldSource; + +import java.util.List; +import java.util.Random; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.stream.IntStream; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class JavatimeTest { + + private static final int NANOS_PER_SECOND = 1000000000; + private static final long t1970 = new java.util.Date(70, 0, 01).getTime(); + private static final Random R = new Random(); + // Data provider contains 10,000 randomized arguments + // which are used as the dates and times for the tests + private static final List DATE_TIME_ARGS = IntStream.range(0, 10_000) + .mapToObj(i -> { + int days = R.nextInt(50) * 365 + R.nextInt(365); + long secs = t1970 + days * 86400 + R.nextInt(86400); + int nanos = R.nextInt(NANOS_PER_SECOND); + int nanos_ms = nanos / 1000000 * 1000000; // millis precision + long millis = secs * 1000 + R.nextInt(1000); + LocalDateTime ldt = LocalDateTime.ofEpochSecond(secs, nanos, ZoneOffset.UTC); + return Arguments.of(millis, nanos, ldt, secs, nanos_ms); + }).toList(); + + @ParameterizedTest(autoCloseArguments = false) + @FieldSource("DATE_TIME_ARGS") + void timestampTest(long millis, int nanos, LocalDateTime ldt, long secs) { + Timestamp ta = new Timestamp(millis); + ta.setNanos(nanos); + assertTrue(isEqual(ta.toLocalDateTime(), ta), + errMsg("j.s.ts -> ldt", millis, nanos, ldt, results(ta.toLocalDateTime(), ta))); + + assertTrue(isEqual(ldt, Timestamp.valueOf(ldt)), + errMsg("ldt -> j.s.ts", millis, nanos, ldt, results(ldt, Timestamp.valueOf(ldt)))); + + Instant inst0 = ta.toInstant(); + assertAll(errMsg("j.s.ts -> instant -> j.s.ts", millis, nanos, ldt), + () -> assertEquals(ta.getTime(), inst0.toEpochMilli()), + () -> assertEquals(ta.getNanos(), inst0.getNano()), + () -> assertEquals(ta, Timestamp.from(inst0)) + ); + + Instant inst = Instant.ofEpochSecond(secs, nanos); + Timestamp ta0 = Timestamp.from(inst); + assertAll(errMsg("instant -> timestamp -> instant", millis, nanos, ldt), + () -> assertEquals(ta0.getTime(), inst.toEpochMilli()), + () -> assertEquals(ta0.getNanos(), inst.getNano()), + () -> assertEquals(inst, ta0.toInstant()) + ); + } + + @ParameterizedTest(autoCloseArguments = false) + @FieldSource("DATE_TIME_ARGS") + void sqlDateTest(long millis, int nanos, LocalDateTime ldt) { + // j.s.d/t uses j.u.d.equals() !!!!!!!! + java.sql.Date jsd = new java.sql.Date(millis); + assertTrue(isEqual(jsd.toLocalDate(), jsd), + errMsg("j.s.d -> ld", millis, nanos, ldt, results(jsd.toLocalDate(), jsd))); + + LocalDate ld = ldt.toLocalDate(); + assertTrue(isEqual(ld, java.sql.Date.valueOf(ld)), + errMsg("ld -> j.s.d", millis, nanos, ldt, results(ld, java.sql.Date.valueOf(ld)))); + } + + @ParameterizedTest(autoCloseArguments = false) + @FieldSource("DATE_TIME_ARGS") + void sqlTimeTest(long millis, int nanos, LocalDateTime ldt, long secs, int nanos_ms) { + java.sql.Time jst = new java.sql.Time(millis); + assertTrue(isEqual(jst.toLocalTime(), jst), + errMsg("j.s.t -> lt", millis, nanos, ldt, results(jst.toLocalTime(), jst))); + + // millis precision + LocalDateTime ldt_ms = LocalDateTime.ofEpochSecond(secs, nanos_ms, ZoneOffset.UTC); + LocalTime lt = ldt_ms.toLocalTime(); + assertTrue(isEqual(lt, java.sql.Time.valueOf(lt)), + errMsg("lt -> j.s.t", millis, nanos, ldt, results(lt, java.sql.Time.valueOf(lt)))); + } + + private static boolean isEqual(LocalDateTime ldt, Timestamp ts) { + ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault()); + return zdt.getYear() == ts.getYear() + 1900 && + zdt.getMonthValue() == ts.getMonth() + 1 && + zdt.getDayOfMonth() == ts.getDate() && + zdt.getHour() == ts.getHours() && + zdt.getMinute() == ts.getMinutes() && + zdt.getSecond() == ts.getSeconds() && + zdt.getNano() == ts.getNanos(); + } + + private static String results(LocalDateTime ldt, Timestamp ts) { + ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault()); + return "ldt:ts %d/%d, %d/%d, %d/%d, %d/%d, %d/%d, %d/%d, nano:[%d/%d]%n".formatted( + zdt.getYear(), ts.getYear() + 1900, + zdt.getMonthValue(), ts.getMonth() + 1, + zdt.getDayOfMonth(), ts.getDate(), + zdt.getHour(), ts.getHours(), + zdt.getMinute(), ts.getMinutes(), + zdt.getSecond(), ts.getSeconds(), + zdt.getNano(), ts.getNanos()); + } + + private static boolean isEqual(LocalDate ld, java.sql.Date d) { + return ld.getYear() == d.getYear() + 1900 && + ld.getMonthValue() == d.getMonth() + 1 && + ld.getDayOfMonth() == d.getDate(); + } + + private static String results(LocalDate ld, java.sql.Date d) { + return "%d/%d, %d/%d, %d/%d%n".formatted( + ld.getYear(), d.getYear() + 1900, + ld.getMonthValue(), d.getMonth() + 1, + ld.getDayOfMonth(), d.getDate()); + } + + private static boolean isEqual(LocalTime lt, java.sql.Time t) { + return lt.getHour() == t.getHours() && + lt.getMinute() == t.getMinutes() && + lt.getSecond() == t.getSeconds(); + } + + private static String results(LocalTime lt, java.sql.Time t) { + return "%d/%d, %d/%d, %d/%d%n".formatted( + lt.getHour(), t.getHours(), + lt.getMinute(), t.getMinutes(), + lt.getSecond(), t.getSeconds()); + } + + private static String errMsg(String testCase, long millis, int nanos, + LocalDateTime ldt, String results) { + return "FAILED: %s%n INPUTS: ms: %16d ns: %10d ldt:[%s]%n ACTUAL: %s" + .formatted(testCase, millis, nanos, ldt, results); + } + + private static String errMsg(String testCase, long millis, int nanos, + LocalDateTime ldt) { + return "FAILED: %s%n INPUTS: ms: %16d ns: %10d ldt:[%s]%n" + .formatted(testCase, millis, nanos, ldt); + } +} \ No newline at end of file diff --git a/test/jdk/java/sql/testng/test/sql/PreparedStatementTests.java b/test/jdk/java/sql/test/sql/PreparedStatementTests.java similarity index 62% rename from test/jdk/java/sql/testng/test/sql/PreparedStatementTests.java rename to test/jdk/java/sql/test/sql/PreparedStatementTests.java index 7813038361d..574b0369b4e 100644 --- a/test/jdk/java/sql/testng/test/sql/PreparedStatementTests.java +++ b/test/jdk/java/sql/test/sql/PreparedStatementTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -20,29 +20,34 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; +import org.junit.jupiter.params.provider.ValueSource; import util.BaseTest; import util.StubConnection; import java.sql.PreparedStatement; import java.sql.SQLException; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class PreparedStatementTests extends BaseTest { private PreparedStatement pstmt; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { pstmt = new StubConnection().prepareStatement("Select * from foo were bar = ?"); } - @AfterMethod + @AfterEach public void tearDownMethod() throws Exception { pstmt.close(); } @@ -51,80 +56,84 @@ public class PreparedStatementTests extends BaseTest { * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedLiteralValues") public void test00(String s, String expected) throws SQLException { - assertEquals(pstmt.enquoteLiteral(s), expected); + assertEquals(expected, pstmt.enquoteLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test01() throws SQLException { - pstmt.enquoteLiteral(null); + assertThrows(NullPointerException.class, () -> pstmt.enquoteLiteral(null)); } /* * Validate that enquoteIdentifier returns the expected value */ - @Test(dataProvider = "validIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedIdentifierValues") public void test02(String s, boolean alwaysQuote, String expected) throws SQLException { - assertEquals(pstmt.enquoteIdentifier(s, alwaysQuote), expected); + assertEquals(expected, pstmt.enquoteIdentifier(s, alwaysQuote)); } /* * Validate that a SQLException is thrown for values that are not valid * for a SQL identifier */ - @Test(dataProvider = "invalidIdentifierValues", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidEnquotedIdentifierValues") public void test03(String s, boolean alwaysQuote) throws SQLException { - pstmt.enquoteIdentifier(s, alwaysQuote); + assertThrows(SQLException.class, () -> pstmt.enquoteIdentifier(s, alwaysQuote)); } /* * Validate a NullPointerException is thrown is the string passed to * enquoteIdentiifer is null */ - @Test(dataProvider = "trueFalse", - expectedExceptions = NullPointerException.class) + @ParameterizedTest(autoCloseArguments = false) + @ValueSource(booleans = {true, false}) public void test04(boolean alwaysQuote) throws SQLException { - pstmt.enquoteIdentifier(null, alwaysQuote); + assertThrows(NullPointerException.class, () -> pstmt.enquoteIdentifier(null, alwaysQuote)); } /* * Validate that isSimpleIdentifier returns the expected value */ - @Test(dataProvider = "simpleIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("simpleIdentifierValues") public void test05(String s, boolean expected) throws SQLException { - assertEquals(pstmt.isSimpleIdentifier(s), expected); + assertEquals(expected, pstmt.isSimpleIdentifier(s)); } /* * Validate a NullPointerException is thrown if the string passed to * isSimpleIdentifier is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test06() throws SQLException { - pstmt.isSimpleIdentifier(null); + assertThrows(NullPointerException.class, () -> pstmt.isSimpleIdentifier(null)); } /* * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedNCharLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedNCharLiteralValues") public void test07(String s, String expected) throws SQLException { - assertEquals(pstmt.enquoteNCharLiteral(s), expected); + assertEquals(expected, pstmt.enquoteNCharLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteNCharLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test08() throws SQLException { - pstmt.enquoteNCharLiteral(null); + assertThrows(NullPointerException.class, () -> pstmt.enquoteNCharLiteral(null)); } } diff --git a/test/jdk/java/sql/testng/test/sql/SQLClientInfoExceptionTests.java b/test/jdk/java/sql/test/sql/SQLClientInfoExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLClientInfoExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLClientInfoExceptionTests.java index 84230b86758..bea35d95c1c 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLClientInfoExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLClientInfoExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,14 +20,16 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.ClientInfoStatus; import java.sql.SQLClientInfoException; import java.sql.SQLException; import java.util.HashMap; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLClientInfoExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLDataExceptionTests.java b/test/jdk/java/sql/test/sql/SQLDataExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLDataExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLDataExceptionTests.java index 8a5f8d1a2c1..a18f6406e66 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLDataExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLDataExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLDataException; import java.sql.SQLException; import java.sql.SQLNonTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLDataExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLExceptionTests.java b/test/jdk/java/sql/test/sql/SQLExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLExceptionTests.java index e3643ef119c..8a7850d8fd9 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,11 +20,13 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLFeatureNotSupportedExceptionTests.java b/test/jdk/java/sql/test/sql/SQLFeatureNotSupportedExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLFeatureNotSupportedExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLFeatureNotSupportedExceptionTests.java index 5b95894412c..24049addfa1 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLFeatureNotSupportedExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLFeatureNotSupportedExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLNonTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLFeatureNotSupportedExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLIntegrityConstraintViolationExceptionTests.java b/test/jdk/java/sql/test/sql/SQLIntegrityConstraintViolationExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLIntegrityConstraintViolationExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLIntegrityConstraintViolationExceptionTests.java index 082e0af15c8..c8c0958e3f3 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLIntegrityConstraintViolationExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLIntegrityConstraintViolationExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLIntegrityConstraintViolationException; import java.sql.SQLNonTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLIntegrityConstraintViolationExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLInvalidAuthorizationSpecExceptionTests.java b/test/jdk/java/sql/test/sql/SQLInvalidAuthorizationSpecExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLInvalidAuthorizationSpecExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLInvalidAuthorizationSpecExceptionTests.java index 6e4eaa567ee..82a9a345a0e 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLInvalidAuthorizationSpecExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLInvalidAuthorizationSpecExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLInvalidAuthorizationSpecException; import java.sql.SQLNonTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLInvalidAuthorizationSpecExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLNonTransientConnectionExceptionTests.java b/test/jdk/java/sql/test/sql/SQLNonTransientConnectionExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLNonTransientConnectionExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLNonTransientConnectionExceptionTests.java index dbd8b685844..fa4c75ed672 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLNonTransientConnectionExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLNonTransientConnectionExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLNonTransientConnectionException; import java.sql.SQLNonTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLNonTransientConnectionExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLNonTransientExceptionTests.java b/test/jdk/java/sql/test/sql/SQLNonTransientExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLNonTransientExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLNonTransientExceptionTests.java index 061ffe20840..0f37f905fa7 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLNonTransientExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLNonTransientExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,12 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLNonTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLNonTransientExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLRecoverableExceptionTests.java b/test/jdk/java/sql/test/sql/SQLRecoverableExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLRecoverableExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLRecoverableExceptionTests.java index d23a131dbcd..d447e08593c 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLRecoverableExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLRecoverableExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,12 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLRecoverableException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLRecoverableExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLSyntaxErrorExceptionTests.java b/test/jdk/java/sql/test/sql/SQLSyntaxErrorExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLSyntaxErrorExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLSyntaxErrorExceptionTests.java index 863213cfcb8..c859f5aa0d2 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLSyntaxErrorExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLSyntaxErrorExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLNonTransientException; import java.sql.SQLSyntaxErrorException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLSyntaxErrorExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLTimeoutExceptionTests.java b/test/jdk/java/sql/test/sql/SQLTimeoutExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLTimeoutExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLTimeoutExceptionTests.java index dfe341545e4..a4e249658d9 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLTimeoutExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLTimeoutExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLTimeoutException; import java.sql.SQLTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLTimeoutExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLTransactionRollbackExceptionTests.java b/test/jdk/java/sql/test/sql/SQLTransactionRollbackExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLTransactionRollbackExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLTransactionRollbackExceptionTests.java index 8453ebe1364..7a7d94cd060 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLTransactionRollbackExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLTransactionRollbackExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLTransactionRollbackException; import java.sql.SQLTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLTransactionRollbackExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLTransientConnectionExceptionTests.java b/test/jdk/java/sql/test/sql/SQLTransientConnectionExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLTransientConnectionExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLTransientConnectionExceptionTests.java index 7999253b1fa..8ac6ef98222 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLTransientConnectionExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLTransientConnectionExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLTransientConnectionException; import java.sql.SQLTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLTransientConnectionExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLTransientExceptionTests.java b/test/jdk/java/sql/test/sql/SQLTransientExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLTransientExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLTransientExceptionTests.java index d86f86b8fc6..4c43b6c693e 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLTransientExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLTransientExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,12 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLTransientExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLWarningTests.java b/test/jdk/java/sql/test/sql/SQLWarningTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLWarningTests.java rename to test/jdk/java/sql/test/sql/SQLWarningTests.java index 2856742dc5c..ab15a877dbd 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLWarningTests.java +++ b/test/jdk/java/sql/test/sql/SQLWarningTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,12 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLWarning; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLWarningTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/StatementTests.java b/test/jdk/java/sql/test/sql/StatementTests.java similarity index 62% rename from test/jdk/java/sql/testng/test/sql/StatementTests.java rename to test/jdk/java/sql/test/sql/StatementTests.java index f2bfe712eb5..42e971cc971 100644 --- a/test/jdk/java/sql/testng/test/sql/StatementTests.java +++ b/test/jdk/java/sql/test/sql/StatementTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -20,14 +20,21 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.Statement; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; -import org.testng.annotations.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import org.junit.jupiter.params.provider.ValueSource; import util.BaseTest; import util.StubConnection; @@ -35,12 +42,12 @@ public class StatementTests extends BaseTest { private Statement stmt; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { stmt = new StubConnection().createStatement(); } - @AfterMethod + @AfterEach public void tearDownMethod() throws Exception { stmt.close(); } @@ -48,80 +55,84 @@ public class StatementTests extends BaseTest { * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedLiteralValues") public void test00(String s, String expected) throws SQLException { - assertEquals(stmt.enquoteLiteral(s), expected); + assertEquals(expected, stmt.enquoteLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test01() throws SQLException { - stmt.enquoteLiteral(null); + assertThrows(NullPointerException.class, () -> stmt.enquoteLiteral(null)); } /* * Validate that enquoteIdentifier returns the expected value */ - @Test(dataProvider = "validIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedIdentifierValues") public void test02(String s, boolean alwaysQuote, String expected) throws SQLException { - assertEquals(stmt.enquoteIdentifier(s, alwaysQuote), expected); + assertEquals(expected, stmt.enquoteIdentifier(s, alwaysQuote)); } /* * Validate that a SQLException is thrown for values that are not valid * for a SQL identifier */ - @Test(dataProvider = "invalidIdentifierValues", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidEnquotedIdentifierValues") public void test03(String s, boolean alwaysQuote) throws SQLException { - stmt.enquoteIdentifier(s, alwaysQuote); + assertThrows(SQLException.class, () -> stmt.enquoteIdentifier(s, alwaysQuote)); } /* * Validate a NullPointerException is thrown is the string passed to * enquoteIdentiifer is null */ - @Test(dataProvider = "trueFalse", - expectedExceptions = NullPointerException.class) + @ParameterizedTest(autoCloseArguments = false) + @ValueSource(booleans = {true, false}) public void test04(boolean alwaysQuote) throws SQLException { - stmt.enquoteIdentifier(null, alwaysQuote); + assertThrows(NullPointerException.class, () -> stmt.enquoteIdentifier(null, alwaysQuote)); } /* * Validate that isSimpleIdentifier returns the expected value */ - @Test(dataProvider = "simpleIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("simpleIdentifierValues") public void test05(String s, boolean expected) throws SQLException { - assertEquals(stmt.isSimpleIdentifier(s), expected); + assertEquals(expected, stmt.isSimpleIdentifier(s)); } /* * Validate a NullPointerException is thrown if the string passed to * isSimpleIdentifier is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test06() throws SQLException { - stmt.isSimpleIdentifier(null); + assertThrows(NullPointerException.class, () -> stmt.isSimpleIdentifier(null)); } /* * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedNCharLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedNCharLiteralValues") public void test07(String s, String expected) throws SQLException { - assertEquals(stmt.enquoteNCharLiteral(s), expected); + assertEquals(expected, stmt.enquoteNCharLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteNCharLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test08() throws SQLException { - stmt.enquoteNCharLiteral(null); + assertThrows(NullPointerException.class, () -> stmt.enquoteNCharLiteral(null)); } } diff --git a/test/jdk/java/sql/testng/test/sql/TimeTests.java b/test/jdk/java/sql/test/sql/TimeTests.java similarity index 76% rename from test/jdk/java/sql/testng/test/sql/TimeTests.java rename to test/jdk/java/sql/test/sql/TimeTests.java index 7b99679754b..71208f03222 100644 --- a/test/jdk/java/sql/testng/test/sql/TimeTests.java +++ b/test/jdk/java/sql/test/sql/TimeTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,13 +20,18 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.Time; import java.time.LocalTime; -import static org.testng.Assert.*; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import util.BaseTest; public class TimeTests extends BaseTest { @@ -34,73 +39,73 @@ public class TimeTests extends BaseTest { /* * Validate an IllegalArgumentException is thrown for calling getYear */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test01() { Time t = Time.valueOf("08:30:59"); - t.getYear(); + assertThrows(IllegalArgumentException.class, t::getYear); } /* * Validate an IllegalArgumentException is thrown for calling getMonth */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test02() { Time t = Time.valueOf("08:30:59"); - t.getMonth(); + assertThrows(IllegalArgumentException.class, t::getMonth); } /* * Validate an IllegalArgumentException is thrown for calling getDay */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test03() { Time t = Time.valueOf("08:30:59"); - t.getDay(); + assertThrows(IllegalArgumentException.class, t::getDay); } /** * Validate an IllegalArgumentException is thrown for calling getDate */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test04() { Time t = Time.valueOf("08:30:59"); - t.getDate(); + assertThrows(IllegalArgumentException.class, t::getDate); } /* * Validate an IllegalArgumentException is thrown for calling setYear */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test05() { Time t = Time.valueOf("08:30:59"); - t.setYear(8); + assertThrows(IllegalArgumentException.class, () -> t.setYear(8)); } /* * Validate an IllegalArgumentException is thrown for calling setMonth */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test06() { Time t = Time.valueOf("08:30:59"); - t.setMonth(8); + assertThrows(IllegalArgumentException.class, () -> t.setMonth(8)); } /* * Validate an IllegalArgumentException is thrown for calling setDate */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test07() { Time t = Time.valueOf("08:30:59"); - t.setDate(30); + assertThrows(IllegalArgumentException.class, () -> t.setDate(30)); } /* * Validate an IllegalArgumentException is thrown for calling getDate */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test08() { Time t = Time.valueOf("08:30:59"); - t.getDate(); + assertThrows(IllegalArgumentException.class, t::getDate); } /* @@ -128,20 +133,20 @@ public class TimeTests extends BaseTest { /* * Validate an NPE occurs when a null LocalDate is passed to valueOf */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test11() throws Exception { LocalTime ld = null; - Time.valueOf(ld); + assertThrows(NullPointerException.class, () -> Time.valueOf(ld)); } /* * Validate an UnsupportedOperationException occurs when toInstant() is * called */ - @Test(expectedExceptions = UnsupportedOperationException.class) + @Test public void test12() throws Exception { Time t = new Time(System.currentTimeMillis()); - t.toInstant(); + assertThrows(UnsupportedOperationException.class, t::toInstant); } /* @@ -149,7 +154,8 @@ public class TimeTests extends BaseTest { * toString() of the other and that the correct value is returned from * toString() */ - @Test(dataProvider = "validTimeValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validTimeValues") public void test13(String time, String expected) { Time t1 = Time.valueOf(time); Time t2 = Time.valueOf(t1.toString()); @@ -182,10 +188,10 @@ public class TimeTests extends BaseTest { /* * Validate an IllegalArgumentException is thrown for an invalid Time string */ - @Test(dataProvider = "invalidTimeValues", - expectedExceptions = IllegalArgumentException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidTimeValues") public void test16(String time) throws Exception { - Time.valueOf(time); + assertThrows(IllegalArgumentException.class, () -> Time.valueOf(time)); } /* @@ -299,29 +305,28 @@ public class TimeTests extends BaseTest { * to validate that an IllegalArgumentException will be thrown from the * valueOf method */ - @DataProvider(name = "invalidTimeValues") - private Object[][] invalidTimeValues() { - return new Object[][]{ - {"2009-11-01 10:50:01"}, - {"1961-08-30 10:50:01.1"}, - {"1961-08-30"}, - {"00:00:00."}, - {"10:50:0.1"}, - {":00:00"}, - {"00::00"}, - {"00:00:"}, - {"::"}, - {" : : "}, - {"0a:00:00"}, - {"00:bb:00"}, - {"00:01:cc"}, - {"08:10:Batman"}, - {"08:10:10:10"}, - {"08:10"}, - {"a:b:c"}, - {null}, - {"8:"} - }; + private Stream invalidTimeValues() { + return Stream.of( + "2009-11-01 10:50:01", + "1961-08-30 10:50:01.1", + "1961-08-30", + "00:00:00.", + "10:50:0.1", + ":00:00", + "00::00", + "00:00:", + "::", + " : : ", + "0a:00:00", + "00:bb:00", + "00:01:cc", + "08:10:Batman", + "08:10:10:10", + "08:10", + "a:b:c", + null, + "8:" + ); } /* @@ -330,19 +335,18 @@ public class TimeTests extends BaseTest { * valueOf method. It also contains the expected return value from * toString() */ - @DataProvider(name = "validTimeValues") - private Object[][] validTimeValues() { - return new Object[][]{ - {"10:50:01", "10:50:01"}, - {"01:1:1", "01:01:01"}, - {"01:01:1", "01:01:01"}, - {"1:01:1", "01:01:01"}, - {"2:02:02", "02:02:02"}, - {"2:02:2", "02:02:02"}, - {"10:50:1", "10:50:01"}, - {"00:00:00", "00:00:00"}, - {"08:30:59", "08:30:59"}, - {"9:0:1", "09:00:01"} - }; + private Stream validTimeValues() { + return Stream.of( + Arguments.of("10:50:01", "10:50:01"), + Arguments.of("01:1:1", "01:01:01"), + Arguments.of("01:01:1", "01:01:01"), + Arguments.of("1:01:1", "01:01:01"), + Arguments.of("2:02:02", "02:02:02"), + Arguments.of("2:02:2", "02:02:02"), + Arguments.of("10:50:1", "10:50:01"), + Arguments.of("00:00:00", "00:00:00"), + Arguments.of("08:30:59", "08:30:59"), + Arguments.of("9:0:1", "09:00:01") + ); } } diff --git a/test/jdk/java/sql/testng/test/sql/TimestampTests.java b/test/jdk/java/sql/test/sql/TimestampTests.java similarity index 73% rename from test/jdk/java/sql/testng/test/sql/TimestampTests.java rename to test/jdk/java/sql/test/sql/TimestampTests.java index 6baea9fa26f..33a58c8d857 100644 --- a/test/jdk/java/sql/testng/test/sql/TimestampTests.java +++ b/test/jdk/java/sql/test/sql/TimestampTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.Date; import java.sql.Time; @@ -30,11 +30,16 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Calendar; import java.util.TimeZone; -import static org.testng.Assert.*; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import util.BaseTest; public class TimestampTests extends BaseTest { @@ -45,7 +50,7 @@ public class TimestampTests extends BaseTest { * Need to set and use a custom TimeZone which does not * observe daylight savings time for this test. */ - @BeforeClass + @BeforeAll public static void setUpClass() throws Exception { defaultTimeZone = TimeZone.getDefault(); TimeZone tzone = TimeZone.getTimeZone("GMT+01"); @@ -56,7 +61,7 @@ public class TimestampTests extends BaseTest { /* * Conservatively reset the default time zone after test. */ - @AfterClass + @AfterAll public static void tearDownClass() throws Exception { TimeZone.setDefault(defaultTimeZone); } @@ -64,10 +69,10 @@ public class TimestampTests extends BaseTest { /* * Validate an IllegalArgumentException is thrown for an invalid Timestamp */ - @Test(dataProvider = "invalidTimestampValues", - expectedExceptions = IllegalArgumentException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidTimestampValues") public void test(String ts) throws Exception { - Timestamp.valueOf(ts); + assertThrows(IllegalArgumentException.class, () -> Timestamp.valueOf(ts)); } /* @@ -80,7 +85,7 @@ public class TimestampTests extends BaseTest { String ExpectedTS = "2009-01-01 10:50:0"; Timestamp ts = Timestamp.valueOf(testTS); Timestamp ts2 = Timestamp.valueOf(ExpectedTS); - assertEquals(ts, ts2, "Error ts1 != ts2"); + assertEquals(ts2, ts, "Error ts1 != ts2"); } /* @@ -91,7 +96,7 @@ public class TimestampTests extends BaseTest { String testTS = "2009-01-01 10:50:0"; Timestamp ts = Timestamp.valueOf(testTS); Timestamp ts2 = Timestamp.valueOf(testTS); - assertEquals(ts, ts2, "Error ts1 != ts2"); + assertEquals(ts2, ts, "Error ts1 != ts2"); } /* @@ -104,7 +109,7 @@ public class TimestampTests extends BaseTest { String ExpectedTS = "2009-01-01 10:50:0"; Timestamp ts = Timestamp.valueOf(testTS); Timestamp ts2 = Timestamp.valueOf(ExpectedTS); - assertEquals(ts, ts2, "Error ts1 != ts2"); + assertEquals(ts2, ts, "Error ts1 != ts2"); } /* @@ -117,7 +122,7 @@ public class TimestampTests extends BaseTest { String ExpectedTS = "2009-01-01 10:50:0"; Timestamp ts = Timestamp.valueOf(testTS); Timestamp ts2 = Timestamp.valueOf(ExpectedTS); - assertEquals(ts, ts2, "Error ts1 != ts2"); + assertEquals(ts2, ts, "Error ts1 != ts2"); } /* @@ -130,7 +135,7 @@ public class TimestampTests extends BaseTest { String ExpectedTS = "2009-01-01 10:50:0"; Timestamp ts = Timestamp.valueOf(testTS); Timestamp ts2 = Timestamp.valueOf(ExpectedTS); - assertEquals(ts, ts2, "Error ts1 != ts2"); + assertEquals(ts2, ts, "Error ts1 != ts2"); } /* @@ -142,7 +147,7 @@ public class TimestampTests extends BaseTest { String ExpectedTS = "2005-01-01 10:20:50.00"; Timestamp ts = Timestamp.valueOf(testTS); Timestamp ts2 = Timestamp.valueOf(ExpectedTS); - assertEquals(ts, ts2, "Error ts1 != ts2"); + assertEquals(ts2, ts, "Error ts1 != ts2"); } /* @@ -219,7 +224,8 @@ public class TimestampTests extends BaseTest { * Validate that two Timestamps are equal when one is created from the * toString() of the other */ - @Test(dataProvider = "validTimestampValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validTimestampValues") public void test13(String ts, String expectedTS) { Timestamp ts1 = Timestamp.valueOf(ts); Timestamp ts2 = Timestamp.valueOf(ts1.toString()); @@ -263,10 +269,10 @@ public class TimestampTests extends BaseTest { * Validate that a NullPointerException is thrown if a null is passed to * the before method */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test17() throws Exception { Timestamp ts1 = Timestamp.valueOf("1996-12-13 14:15:25.745634"); - ts1.before(null); + assertThrows(NullPointerException.class, () -> ts1.before(null)); } /* @@ -316,10 +322,10 @@ public class TimestampTests extends BaseTest { * Validate that a NullPointerException is thrown if a null is passed to the * after method */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test22() throws Exception { Timestamp ts1 = Timestamp.valueOf("1966-08-30 08:08:08"); - ts1.after(null); + assertThrows(NullPointerException.class, () -> ts1.after(null)); } /* @@ -476,21 +482,21 @@ public class TimestampTests extends BaseTest { /* * Validate an IllegalArgumentException is thrown for an invalid nanos value */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test38() throws Exception { Timestamp ts1 = Timestamp.valueOf("1961-08-30 00:00:00"); - ts1.setNanos(-1); + assertThrows(IllegalArgumentException.class, () -> ts1.setNanos(-1)); } /* * Validate an IllegalArgumentException is thrown for an invalid nanos value */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test39() throws Exception { int nanos = 999999999; Timestamp ts1 = Timestamp.valueOf("1961-08-30 00:00:00"); - ts1.setNanos(nanos + 1); + assertThrows(IllegalArgumentException.class, () -> ts1.setNanos(nanos + 1)); } /* @@ -541,10 +547,10 @@ public class TimestampTests extends BaseTest { /* * Validate an NPE occurs when a null LocalDateTime is passed to valueOF */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test44() throws Exception { LocalDateTime ldt = null; - Timestamp.valueOf(ldt); + assertThrows(NullPointerException.class, () -> Timestamp.valueOf(ldt)); } /* @@ -572,10 +578,10 @@ public class TimestampTests extends BaseTest { /* * Validate an NPE occurs when a null instant is passed to from */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test47() throws Exception { Instant instant = null; - Timestamp.from(instant); + assertThrows(NullPointerException.class, () -> Timestamp.from(instant)); } // Added SQE tests @@ -628,7 +634,8 @@ public class TimestampTests extends BaseTest { * Validate that two Timestamps are equal when one is created from the * toString() of the other */ - @Test(dataProvider = "validateNanos") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validateNanos") public void test51(String ts, int nanos) { Timestamp ts1 = Timestamp.valueOf(ts); Timestamp ts2 = Timestamp.valueOf(ts1.toString()); @@ -636,35 +643,36 @@ public class TimestampTests extends BaseTest { "Error with Nanos"); } - @Test(dataProvider = "validTimestampLongValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validTimestampLongValues") public void test52(long value, String ts) { Timestamp ts1 = new Timestamp(value); - assertEquals(ts1.toString(), ts, "ts1.toString() != ts"); + assertEquals(ts, ts1.toString(), "ts1.toString() != ts"); } @Test public void test53() { // The latest Instant that can be converted to a Timestamp. Instant instant1 = Instant.ofEpochSecond(Long.MAX_VALUE / 1000, 999_999_999); - assertEquals(Timestamp.from(instant1).toInstant(), instant1); + assertEquals(instant1, Timestamp.from(instant1).toInstant()); // One nanosecond more, and converting it gets an overflow. Instant instant2 = instant1.plusNanos(1); - expectThrows(IllegalArgumentException.class, () -> Timestamp.from(instant2)); + assertThrows(IllegalArgumentException.class, () -> Timestamp.from(instant2)); // The earliest Instant that can be converted to a Timestamp. Instant instant3 = Instant.ofEpochSecond(Long.MIN_VALUE / 1000, 0); - assertEquals(Timestamp.from(instant3).toInstant(), instant3); + assertEquals(instant3, Timestamp.from(instant3).toInstant()); // One nanosecond less, and converting it gets an overflow. Instant instant4 = instant3.minusNanos(1); - expectThrows(IllegalArgumentException.class, () -> Timestamp.from(instant4)); + assertThrows(IllegalArgumentException.class, () -> Timestamp.from(instant4)); // The latest possible Instant will certainly overflow. - expectThrows(IllegalArgumentException.class, () -> Timestamp.from(Instant.MAX)); + assertThrows(IllegalArgumentException.class, () -> Timestamp.from(Instant.MAX)); // The earliest possible Instant will certainly overflow. - expectThrows(IllegalArgumentException.class, () -> Timestamp.from(Instant.MIN)); + assertThrows(IllegalArgumentException.class, () -> Timestamp.from(Instant.MIN)); } /* @@ -683,7 +691,7 @@ public class TimestampTests extends BaseTest { assertTrue(ts1.equals(ts2)); // As the Timestamp values, including the nanos are the same, the hashCode's // should be equal - assertEquals(ts1.hashCode(), ts2.hashCode()); + assertEquals(ts2.hashCode(), ts1.hashCode()); } /* @@ -702,7 +710,7 @@ public class TimestampTests extends BaseTest { assertTrue(ts2.equals(ts2)); assertFalse(ts1.equals(ts2)); // As the nanos differ, the hashCode values should differ - assertNotEquals(ts1.hashCode(), ts2.hashCode()); + assertNotEquals(ts2.hashCode(), ts1.hashCode()); } /* @@ -710,33 +718,32 @@ public class TimestampTests extends BaseTest { * to validate that an IllegalArgumentException will be thrown from the * valueOf method */ - @DataProvider(name = "invalidTimestampValues") - private Object[][] invalidTimestampValues() { - return new Object[][]{ - {"2009-11-01-01 10:50:01"}, - {"aaaa-11-01-01 10:50"}, - {"aaaa-11-01 10:50"}, - {"1961--30 00:00:00"}, - {"--30 00:00:00"}, - {"-- 00:00:00"}, - {"1961-1- 00:00:00"}, - {"2009-11-01"}, - {"10:50:01"}, - {"1961-a-30 00:00:00"}, - {"1961-01-bb 00:00:00"}, - {"1961-08-30 00:00:00."}, - {"1961-08-30 :00:00"}, - {"1961-08-30 00::00"}, - {"1961-08-30 00:00:"}, - {"1961-08-30 ::"}, - {"1961-08-30 0a:00:00"}, - {"1961-08-30 00:bb:00"}, - {"1961-08-30 00:01:cc"}, - {"1961-08-30 00:00:00.01a"}, - {"1961-08-30 00:00:00.a"}, - {"1996-12-10 12:26:19.1234567890"}, - {null} - }; + private Stream invalidTimestampValues() { + return Stream.of( + "2009-11-01-01 10:50:01", + "aaaa-11-01-01 10:50", + "aaaa-11-01 10:50", + "1961--30 00:00:00", + "--30 00:00:00", + "-- 00:00:00", + "1961-1- 00:00:00", + "2009-11-01", + "10:50:01", + "1961-a-30 00:00:00", + "1961-01-bb 00:00:00", + "1961-08-30 00:00:00.", + "1961-08-30 :00:00", + "1961-08-30 00::00", + "1961-08-30 00:00:", + "1961-08-30 ::", + "1961-08-30 0a:00:00", + "1961-08-30 00:bb:00", + "1961-08-30 00:01:cc", + "1961-08-30 00:00:00.01a", + "1961-08-30 00:00:00.a", + "1996-12-10 12:26:19.1234567890", + null + ); } /* @@ -744,67 +751,65 @@ public class TimestampTests extends BaseTest { * to validate that an IllegalArgumentException will not be thrown from the * valueOf method and the corect value from toString() is returned */ - @DataProvider(name = "validTimestampValues") - private Object[][] validTimestampValues() { - return new Object[][]{ - {"1961-08-30 00:00:00", "1961-08-30 00:00:00.0"}, - {"1961-08-30 11:22:33", "1961-08-30 11:22:33.0"}, - {"1961-8-30 00:00:00", "1961-08-30 00:00:00.0"}, - {"1966-08-1 00:00:00", "1966-08-01 00:00:00.0"}, - {"1996-12-10 12:26:19.1", "1996-12-10 12:26:19.1"}, - {"1996-12-10 12:26:19.12", "1996-12-10 12:26:19.12"}, - {"1996-12-10 12:26:19.123", "1996-12-10 12:26:19.123"}, - {"1996-12-10 12:26:19.1234", "1996-12-10 12:26:19.1234"}, - {"1996-12-10 12:26:19.12345", "1996-12-10 12:26:19.12345"}, - {"1996-12-10 12:26:19.123456", "1996-12-10 12:26:19.123456"}, - {"1996-12-10 12:26:19.1234567", "1996-12-10 12:26:19.1234567"}, - {"1996-12-10 12:26:19.12345678", "1996-12-10 12:26:19.12345678"}, - {"1996-12-10 12:26:19.123456789", "1996-12-10 12:26:19.123456789"}, - {"1996-12-10 12:26:19.000000001", "1996-12-10 12:26:19.000000001"}, - {"1996-12-10 12:26:19.000000012", "1996-12-10 12:26:19.000000012"}, - {"1996-12-10 12:26:19.000000123", "1996-12-10 12:26:19.000000123"}, - {"1996-12-10 12:26:19.000001234", "1996-12-10 12:26:19.000001234"}, - {"1996-12-10 12:26:19.000012345", "1996-12-10 12:26:19.000012345"}, - {"1996-12-10 12:26:19.000123456", "1996-12-10 12:26:19.000123456"}, - {"1996-12-10 12:26:19.001234567", "1996-12-10 12:26:19.001234567"}, - {"1996-12-10 12:26:19.12345678", "1996-12-10 12:26:19.12345678"}, - {"1996-12-10 12:26:19.0", "1996-12-10 12:26:19.0"}, - {"1996-12-10 12:26:19.01230", "1996-12-10 12:26:19.0123"} - }; + private Stream validTimestampValues() { + return Stream.of( + Arguments.of("1961-08-30 00:00:00", "1961-08-30 00:00:00.0"), + Arguments.of("1961-08-30 11:22:33", "1961-08-30 11:22:33.0"), + Arguments.of("1961-8-30 00:00:00", "1961-08-30 00:00:00.0"), + Arguments.of("1966-08-1 00:00:00", "1966-08-01 00:00:00.0"), + Arguments.of("1996-12-10 12:26:19.1", "1996-12-10 12:26:19.1"), + Arguments.of("1996-12-10 12:26:19.12", "1996-12-10 12:26:19.12"), + Arguments.of("1996-12-10 12:26:19.123", "1996-12-10 12:26:19.123"), + Arguments.of("1996-12-10 12:26:19.1234", "1996-12-10 12:26:19.1234"), + Arguments.of("1996-12-10 12:26:19.12345", "1996-12-10 12:26:19.12345"), + Arguments.of("1996-12-10 12:26:19.123456", "1996-12-10 12:26:19.123456"), + Arguments.of("1996-12-10 12:26:19.1234567", "1996-12-10 12:26:19.1234567"), + Arguments.of("1996-12-10 12:26:19.12345678", "1996-12-10 12:26:19.12345678"), + Arguments.of("1996-12-10 12:26:19.123456789", "1996-12-10 12:26:19.123456789"), + Arguments.of("1996-12-10 12:26:19.000000001", "1996-12-10 12:26:19.000000001"), + Arguments.of("1996-12-10 12:26:19.000000012", "1996-12-10 12:26:19.000000012"), + Arguments.of("1996-12-10 12:26:19.000000123", "1996-12-10 12:26:19.000000123"), + Arguments.of("1996-12-10 12:26:19.000001234", "1996-12-10 12:26:19.000001234"), + Arguments.of("1996-12-10 12:26:19.000012345", "1996-12-10 12:26:19.000012345"), + Arguments.of("1996-12-10 12:26:19.000123456", "1996-12-10 12:26:19.000123456"), + Arguments.of("1996-12-10 12:26:19.001234567", "1996-12-10 12:26:19.001234567"), + Arguments.of("1996-12-10 12:26:19.12345678", "1996-12-10 12:26:19.12345678"), + Arguments.of("1996-12-10 12:26:19.0", "1996-12-10 12:26:19.0"), + Arguments.of("1996-12-10 12:26:19.01230", "1996-12-10 12:26:19.0123") + ); } - @DataProvider(name = "validTimestampLongValues") - private Object[][] validTimestampLongValues() { - return new Object[][]{ - {1L, "1970-01-01 01:00:00.001"}, - {-3600*1000L - 1, "1969-12-31 23:59:59.999"}, - {-(20000L*365*24*60*60*1000), "18018-08-28 01:00:00.0"}, - {Timestamp.valueOf("1961-08-30 11:22:33").getTime(), "1961-08-30 11:22:33.0"}, - {Timestamp.valueOf("1961-08-30 11:22:33.54321000").getTime(), "1961-08-30 11:22:33.543"}, // nanoprecision lost - {new Timestamp(114, 10, 10, 10, 10, 10, 100000000).getTime(), "2014-11-10 10:10:10.1"}, - {new Timestamp(0, 10, 10, 10, 10, 10, 100000).getTime(), "1900-11-10 10:10:10.0"}, // nanoprecision lost - {new Date(114, 10, 10).getTime(), "2014-11-10 00:00:00.0"}, - {new Date(0, 10, 10).getTime(), "1900-11-10 00:00:00.0"}, - {LocalDateTime.of(1960, 10, 10, 10, 10, 10, 50000).atZone(ZoneId.of("America/Los_Angeles")) - .toInstant().toEpochMilli(), "1960-10-10 19:10:10.0"}, + private Stream validTimestampLongValues() { + return Stream.of( + Arguments.of(1L, "1970-01-01 01:00:00.001"), + Arguments.of(-3600*1000L - 1, "1969-12-31 23:59:59.999"), + Arguments.of(-(20000L*365*24*60*60*1000), "18018-08-28 01:00:00.0"), + Arguments.of(Timestamp.valueOf("1961-08-30 11:22:33").getTime(), "1961-08-30 11:22:33.0"), + Arguments.of(Timestamp.valueOf("1961-08-30 11:22:33.54321000").getTime(), "1961-08-30 11:22:33.543"), // nanoprecision lost + Arguments.of(new Timestamp(114, 10, 10, 10, 10, 10, 100000000).getTime(), "2014-11-10 10:10:10.1"), + Arguments.of(new Timestamp(0, 10, 10, 10, 10, 10, 100000).getTime(), "1900-11-10 10:10:10.0"), // nanoprecision lost + Arguments.of(new Date(114, 10, 10).getTime(), "2014-11-10 00:00:00.0"), + Arguments.of(new Date(0, 10, 10).getTime(), "1900-11-10 00:00:00.0"), + Arguments.of(LocalDateTime.of(1960, 10, 10, 10, 10, 10, 50000).atZone(ZoneId.of("America/Los_Angeles")) + .toInstant().toEpochMilli(), "1960-10-10 19:10:10.0"), // millisecond timestamps wraps around at year 1, so Long.MIN_VALUE looks similar // Long.MAX_VALUE, while actually representing 292278994 BCE - {Long.MIN_VALUE, "292278994-08-17 08:12:55.192"}, - {Long.MAX_VALUE + 1, "292278994-08-17 08:12:55.192"}, - {Long.MAX_VALUE, "292278994-08-17 08:12:55.807"}, - {Long.MIN_VALUE - 1, "292278994-08-17 08:12:55.807"}, + Arguments.of(Long.MIN_VALUE, "292278994-08-17 08:12:55.192"), + Arguments.of(Long.MAX_VALUE + 1, "292278994-08-17 08:12:55.192"), + Arguments.of(Long.MAX_VALUE, "292278994-08-17 08:12:55.807"), + Arguments.of(Long.MIN_VALUE - 1, "292278994-08-17 08:12:55.807"), // wrap around point near 0001-01-01, test that we never get a negative year: - {-(1970L*365*24*60*60*1000), "0001-04-25 01:00:00.0"}, - {-(1970L*365*24*60*60*1000 + 115*24*60*60*1000L), "0001-12-31 01:00:00.0"}, - {-(1970L*365*24*60*60*1000 + 115*24*60*60*1000L - 23*60*60*1000L), "0001-01-01 00:00:00.0"}, + Arguments.of(-(1970L*365*24*60*60*1000), "0001-04-25 01:00:00.0"), + Arguments.of(-(1970L*365*24*60*60*1000 + 115*24*60*60*1000L), "0001-12-31 01:00:00.0"), + Arguments.of(-(1970L*365*24*60*60*1000 + 115*24*60*60*1000L - 23*60*60*1000L), "0001-01-01 00:00:00.0"), - {LocalDateTime.of(0, 1, 1, 10, 10, 10, 50000).atZone(ZoneId.of("America/Los_Angeles")) - .toInstant().toEpochMilli() - 2*24*60*60*1000L, "0001-01-01 19:03:08.0"}, // 1 BCE - {LocalDateTime.of(0, 1, 1, 10, 10, 10, 50000).atZone(ZoneId.of("America/Los_Angeles")) - .toInstant().toEpochMilli() - 3*24*60*60*1000L, "0002-12-31 19:03:08.0"} // 2 BCE - }; + Arguments.of(LocalDateTime.of(0, 1, 1, 10, 10, 10, 50000).atZone(ZoneId.of("America/Los_Angeles")) + .toInstant().toEpochMilli() - 2*24*60*60*1000L, "0001-01-01 19:03:08.0"), // 1 BCE + Arguments.of(LocalDateTime.of(0, 1, 1, 10, 10, 10, 50000).atZone(ZoneId.of("America/Los_Angeles")) + .toInstant().toEpochMilli() - 3*24*60*60*1000L, "0002-12-31 19:03:08.0") // 2 BCE + ); } /* @@ -812,29 +817,28 @@ public class TimestampTests extends BaseTest { * validate that the correct Nanos value is generated from the specified * Timestamp */ - @DataProvider(name = "validateNanos") - private Object[][] validateNanos() { - return new Object[][]{ - {"1961-08-30 00:00:00", 0}, - {"1996-12-10 12:26:19.1", 100000000}, - {"1996-12-10 12:26:19.12", 120000000}, - {"1996-12-10 12:26:19.123", 123000000}, - {"1996-12-10 12:26:19.1234", 123400000}, - {"1996-12-10 12:26:19.12345", 123450000}, - {"1996-12-10 12:26:19.123456", 123456000}, - {"1996-12-10 12:26:19.1234567", 123456700}, - {"1996-12-10 12:26:19.12345678", 123456780}, - {"1996-12-10 12:26:19.123456789", 123456789}, - {"1996-12-10 12:26:19.000000001", 1}, - {"1996-12-10 12:26:19.000000012", 12}, - {"1996-12-10 12:26:19.000000123", 123}, - {"1996-12-10 12:26:19.000001234", 1234}, - {"1996-12-10 12:26:19.000012345", 12345}, - {"1996-12-10 12:26:19.000123456", 123456}, - {"1996-12-10 12:26:19.001234567", 1234567}, - {"1996-12-10 12:26:19.012345678", 12345678}, - {"1996-12-10 12:26:19.0", 0}, - {"1996-12-10 12:26:19.01230", 12300000} - }; + private Stream validateNanos() { + return Stream.of( + Arguments.of("1961-08-30 00:00:00", 0), + Arguments.of("1996-12-10 12:26:19.1", 100000000), + Arguments.of("1996-12-10 12:26:19.12", 120000000), + Arguments.of("1996-12-10 12:26:19.123", 123000000), + Arguments.of("1996-12-10 12:26:19.1234", 123400000), + Arguments.of("1996-12-10 12:26:19.12345", 123450000), + Arguments.of("1996-12-10 12:26:19.123456", 123456000), + Arguments.of("1996-12-10 12:26:19.1234567", 123456700), + Arguments.of("1996-12-10 12:26:19.12345678", 123456780), + Arguments.of("1996-12-10 12:26:19.123456789", 123456789), + Arguments.of("1996-12-10 12:26:19.000000001", 1), + Arguments.of("1996-12-10 12:26:19.000000012", 12), + Arguments.of("1996-12-10 12:26:19.000000123", 123), + Arguments.of("1996-12-10 12:26:19.000001234", 1234), + Arguments.of("1996-12-10 12:26:19.000012345", 12345), + Arguments.of("1996-12-10 12:26:19.000123456", 123456), + Arguments.of("1996-12-10 12:26:19.001234567", 1234567), + Arguments.of("1996-12-10 12:26:19.012345678", 12345678), + Arguments.of("1996-12-10 12:26:19.0", 0), + Arguments.of("1996-12-10 12:26:19.01230", 12300000) + ); } } diff --git a/test/jdk/java/sql/testng/test/sql/othervm/DriverManagerInitTests.java b/test/jdk/java/sql/test/sql/drivermanager/DriverManagerInitTests.java similarity index 93% rename from test/jdk/java/sql/testng/test/sql/othervm/DriverManagerInitTests.java rename to test/jdk/java/sql/test/sql/drivermanager/DriverManagerInitTests.java index d2570993533..f77fde4b166 100644 --- a/test/jdk/java/sql/testng/test/sql/othervm/DriverManagerInitTests.java +++ b/test/jdk/java/sql/test/sql/drivermanager/DriverManagerInitTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql.othervm; +package sql.drivermanager; import java.io.BufferedReader; import java.io.CharArrayReader; @@ -33,8 +33,8 @@ import java.util.Enumeration; import java.util.logging.Level; import java.util.logging.Logger; -import static org.testng.Assert.*; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; public class DriverManagerInitTests { diff --git a/test/jdk/java/sql/driverModuleTests/DriverManagerModuleTests.java b/test/jdk/java/sql/test/sql/drivermanager/DriverManagerModuleTests.java similarity index 84% rename from test/jdk/java/sql/driverModuleTests/DriverManagerModuleTests.java rename to test/jdk/java/sql/test/sql/drivermanager/DriverManagerModuleTests.java index aad39cdb1dd..083153ac38f 100644 --- a/test/jdk/java/sql/driverModuleTests/DriverManagerModuleTests.java +++ b/test/jdk/java/sql/test/sql/drivermanager/DriverManagerModuleTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, 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 @@ -20,23 +20,19 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +package sql.drivermanager; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; -import static org.testng.Assert.*; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /* * @test - * @library /java/sql/modules * @build luckydogdriver/* mystubdriver/* - * @run testng/othervm DriverManagerModuleTests * @summary Tests that a JDBC Driver that is a module can be loaded * via the service-provider loading mechanism. */ @@ -46,28 +42,12 @@ public class DriverManagerModuleTests { private static final String STUBDRIVERURL = "jdbc:stub:myDB"; private static final String CONNECTION_CLASS_NAME = "com.luckydogtennis.StubConnection"; - @BeforeClass - public static void setUpClass() throws Exception { - } - - @AfterClass - public static void tearDownClass() throws Exception { - } - - @BeforeMethod - public void setUpMethod() throws Exception { - } - - @AfterMethod - public void tearDownMethod() throws Exception { - } - /** * Validate JDBC drivers as modules will be accessible. One driver will be * loaded and registered via the service-provider loading mechanism. The * other driver will need to be explictly loaded * - * @throws java.lang.Exception + * @throws Exception */ @Test public void test() throws Exception { @@ -101,7 +81,7 @@ public class DriverManagerModuleTests { * Validate that a Connection can be obtained from a JDBC driver which is a * module and loaded via the service-provider loading mechanism. * - * @throws java.lang.Exception + * @throws Exception */ @Test public void test00() throws Exception { diff --git a/test/jdk/java/sql/test/sql/drivermanager/TEST.properties b/test/jdk/java/sql/test/sql/drivermanager/TEST.properties new file mode 100644 index 00000000000..0fed686adad --- /dev/null +++ b/test/jdk/java/sql/test/sql/drivermanager/TEST.properties @@ -0,0 +1,4 @@ +# drivermanager tests are run in othervm +othervm.dirs = . +# Required by DriverManagerModuleTests.java +lib.dirs = /java/sql/modules diff --git a/test/jdk/java/sql/testng/TEST.properties b/test/jdk/java/sql/testng/TEST.properties deleted file mode 100644 index 66b1566374a..00000000000 --- a/test/jdk/java/sql/testng/TEST.properties +++ /dev/null @@ -1,4 +0,0 @@ -# JDBC unit tests uses TestNG -TestNG.dirs = . -othervm.dirs = test/sql/othervm - diff --git a/test/jdk/java/sql/testng/util/BaseTest.java b/test/jdk/java/sql/util/BaseTest.java similarity index 54% rename from test/jdk/java/sql/testng/util/BaseTest.java rename to test/jdk/java/sql/util/BaseTest.java index 8751183726d..1a1b14e795a 100644 --- a/test/jdk/java/sql/testng/util/BaseTest.java +++ b/test/jdk/java/sql/util/BaseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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,9 +29,12 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.sql.JDBCType; import java.sql.SQLException; +import java.util.stream.Stream; -import org.testng.annotations.DataProvider; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.provider.Arguments; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class BaseTest { protected final String reason = "reason"; @@ -73,29 +76,11 @@ public class BaseTest { return o1; } - /* - * DataProvider used to specify the value to set and check for - * methods using boolean values - */ - @DataProvider(name = "trueFalse") - protected Object[][] trueFalse() { - return new Object[][]{ - {true}, - {false} - }; - } - /* * DataProvider used to specify the standard JDBC Types */ - @DataProvider(name = "jdbcTypes") - protected Object[][] jdbcTypes() { - Object[][] o = new Object[JDBCType.values().length][1]; - int pos = 0; - for (JDBCType c : JDBCType.values()) { - o[pos++][0] = c.getVendorTypeNumber(); - } - return o; + protected Stream jdbcTypes() { + return Stream.of(JDBCType.values()).map(JDBCType::getVendorTypeNumber); } /* @@ -103,15 +88,14 @@ public class BaseTest { * that enquoteLiteral converts a string to a literal and every instance of * a single quote will be converted into two single quotes in the literal. */ - @DataProvider(name = "validEnquotedLiteralValues") - protected Object[][] validEnquotedLiteralValues() { - return new Object[][]{ - {"Hello", "'Hello'"}, - {"G'Day", "'G''Day'"}, - {"'G''Day'", "'''G''''Day'''"}, - {"I'''M", "'I''''''M'"}, - {"The Dark Knight", "'The Dark Knight'"}, - }; + protected Stream validEnquotedLiteralValues() { + return Stream.of( + Arguments.of("Hello", "'Hello'"), + Arguments.of("G'Day", "'G''Day'"), + Arguments.of("'G''Day'", "'''G''''Day'''"), + Arguments.of("I'''M", "'I''''''M'"), + Arguments.of("The Dark Knight", "'The Dark Knight'") + ); } /* @@ -119,37 +103,37 @@ public class BaseTest { * that enqouteIdentifier returns a simple SQL Identifier or a * quoted identifier */ - @DataProvider(name = "validIdentifierValues") - protected Object[][] validEnquotedIdentifierValues() { - return new Object[][]{ - {"b", false, "b"}, - {"b", true, "\"b\""}, - {MAX_LENGTH_IDENTIFIER, false, MAX_LENGTH_IDENTIFIER}, - {MAX_LENGTH_IDENTIFIER, true, "\"" + MAX_LENGTH_IDENTIFIER + "\""}, - {"Hello", false, "Hello"}, - {"Hello", true, "\"Hello\""}, - {"G'Day", false, "\"G'Day\""}, - {"G'Day", true, "\"G'Day\""}, - {"Bruce Wayne", false, "\"Bruce Wayne\""}, - {"Bruce Wayne", true, "\"Bruce Wayne\""}, - {"select", false, "\"select\""}, - {"table", true, "\"table\""}, - {"GoodDay$", false, "\"GoodDay$\""}, - {"GoodDay$", true, "\"GoodDay$\""},}; + protected Stream validEnquotedIdentifierValues() { + return Stream.of( + Arguments.of("b", false, "b"), + Arguments.of("b", true, "\"b\""), + Arguments.of(MAX_LENGTH_IDENTIFIER, false, MAX_LENGTH_IDENTIFIER), + Arguments.of(MAX_LENGTH_IDENTIFIER, true, "\"" + MAX_LENGTH_IDENTIFIER + "\""), + Arguments.of("Hello", false, "Hello"), + Arguments.of("Hello", true, "\"Hello\""), + Arguments.of("G'Day", false, "\"G'Day\""), + Arguments.of("G'Day", true, "\"G'Day\""), + Arguments.of("Bruce Wayne", false, "\"Bruce Wayne\""), + Arguments.of("Bruce Wayne", true, "\"Bruce Wayne\""), + Arguments.of("select", false, "\"select\""), + Arguments.of("table", true, "\"table\""), + Arguments.of("GoodDay$", false, "\"GoodDay$\""), + Arguments.of("GoodDay$", true, "\"GoodDay$\"") + ); } /* * DataProvider used to provide strings are invalid for enquoteIdentifier * resulting in a SQLException being thrown */ - @DataProvider(name = "invalidIdentifierValues") - protected Object[][] invalidEnquotedIdentifierValues() { - return new Object[][]{ - {"Hel\"lo", false}, - {"\"Hel\"lo\"", true}, - {"Hello" + '\0', false}, - {"", false}, - {MAX_LENGTH_IDENTIFIER + 'a', false},}; + protected Stream invalidEnquotedIdentifierValues() { + return Stream.of( + Arguments.of("Hel\"lo", false), + Arguments.of("\"Hel\"lo\"", true), + Arguments.of("Hello" + '\0', false), + Arguments.of("", false), + Arguments.of(MAX_LENGTH_IDENTIFIER + 'a', false) + ); } /* @@ -157,22 +141,21 @@ public class BaseTest { * that isSimpleIdentifier returns the correct value based on the * identifier specified. */ - @DataProvider(name = "simpleIdentifierValues") - protected Object[][] simpleIdentifierValues() { - return new Object[][]{ - {"b", true}, - {"Hello", true}, - {"\"Gotham\"", false}, - {"G'Day", false}, - {"Bruce Wayne", false}, - {"GoodDay$", false}, - {"Dick_Grayson", true}, - {"Batmobile1966", true}, - {MAX_LENGTH_IDENTIFIER, true}, - {MAX_LENGTH_IDENTIFIER + 'a', false}, - {"", false}, - {"select", false} - }; + protected Stream simpleIdentifierValues() { + return Stream.of( + Arguments.of("b", true), + Arguments.of("Hello", true), + Arguments.of("\"Gotham\"", false), + Arguments.of("G'Day", false), + Arguments.of("Bruce Wayne", false), + Arguments.of("GoodDay$", false), + Arguments.of("Dick_Grayson", true), + Arguments.of("Batmobile1966", true), + Arguments.of(MAX_LENGTH_IDENTIFIER, true), + Arguments.of(MAX_LENGTH_IDENTIFIER + 'a', false), + Arguments.of("", false), + Arguments.of("select", false) + ); } /* @@ -181,15 +164,14 @@ public class BaseTest { * literal and every instance of * a single quote will be converted into two single quotes in the literal. */ - @DataProvider(name = "validEnquotedNCharLiteralValues") - protected Object[][] validEnquotedNCharLiteralValues() { - return new Object[][]{ - {"Hello", "N'Hello'"}, - {"G'Day", "N'G''Day'"}, - {"'G''Day'", "N'''G''''Day'''"}, - {"I'''M", "N'I''''''M'"}, - {"N'Hello'", "N'N''Hello'''"}, - {"The Dark Knight", "N'The Dark Knight'"} - }; + protected Stream validEnquotedNCharLiteralValues() { + return Stream.of( + Arguments.of("Hello", "N'Hello'"), + Arguments.of("G'Day", "N'G''Day'"), + Arguments.of("'G''Day'", "N'''G''''Day'''"), + Arguments.of("I'''M", "N'I''''''M'"), + Arguments.of("N'Hello'", "N'N''Hello'''"), + Arguments.of("The Dark Knight", "N'The Dark Knight'") + ); } } diff --git a/test/jdk/java/sql/testng/util/DriverActionImpl.java b/test/jdk/java/sql/util/DriverActionImpl.java similarity index 94% rename from test/jdk/java/sql/testng/util/DriverActionImpl.java rename to test/jdk/java/sql/util/DriverActionImpl.java index 4a286ad8f3c..48fed8086a7 100644 --- a/test/jdk/java/sql/testng/util/DriverActionImpl.java +++ b/test/jdk/java/sql/util/DriverActionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/java/sql/testng/util/SerializedBatchUpdateException.java b/test/jdk/java/sql/util/SerializedBatchUpdateException.java similarity index 99% rename from test/jdk/java/sql/testng/util/SerializedBatchUpdateException.java rename to test/jdk/java/sql/util/SerializedBatchUpdateException.java index 00efc5275a7..08473e6a184 100644 --- a/test/jdk/java/sql/testng/util/SerializedBatchUpdateException.java +++ b/test/jdk/java/sql/util/SerializedBatchUpdateException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/java/sql/testng/util/StubCallableStatement.java b/test/jdk/java/sql/util/StubCallableStatement.java similarity index 99% rename from test/jdk/java/sql/testng/util/StubCallableStatement.java rename to test/jdk/java/sql/util/StubCallableStatement.java index 4a7c27314bb..4700699c4ba 100644 --- a/test/jdk/java/sql/testng/util/StubCallableStatement.java +++ b/test/jdk/java/sql/util/StubCallableStatement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 diff --git a/test/jdk/java/sql/testng/util/StubConnection.java b/test/jdk/java/sql/util/StubConnection.java similarity index 99% rename from test/jdk/java/sql/testng/util/StubConnection.java rename to test/jdk/java/sql/util/StubConnection.java index cd013572adc..e1a60efc323 100644 --- a/test/jdk/java/sql/testng/util/StubConnection.java +++ b/test/jdk/java/sql/util/StubConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/java/sql/testng/util/StubDatabaseMetaData.java b/test/jdk/java/sql/util/StubDatabaseMetaData.java similarity index 99% rename from test/jdk/java/sql/testng/util/StubDatabaseMetaData.java rename to test/jdk/java/sql/util/StubDatabaseMetaData.java index 3bf70afaa8f..3c95ac94793 100644 --- a/test/jdk/java/sql/testng/util/StubDatabaseMetaData.java +++ b/test/jdk/java/sql/util/StubDatabaseMetaData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 diff --git a/test/jdk/java/sql/testng/util/StubDriver.java b/test/jdk/java/sql/util/StubDriver.java similarity index 96% rename from test/jdk/java/sql/testng/util/StubDriver.java rename to test/jdk/java/sql/util/StubDriver.java index 12253080377..c45b9ec9919 100644 --- a/test/jdk/java/sql/testng/util/StubDriver.java +++ b/test/jdk/java/sql/util/StubDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/java/sql/testng/util/StubDriverDA.java b/test/jdk/java/sql/util/StubDriverDA.java similarity index 96% rename from test/jdk/java/sql/testng/util/StubDriverDA.java rename to test/jdk/java/sql/util/StubDriverDA.java index 5b23f0846d8..f134a9d28cf 100644 --- a/test/jdk/java/sql/testng/util/StubDriverDA.java +++ b/test/jdk/java/sql/util/StubDriverDA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/java/sql/testng/util/StubPreparedStatement.java b/test/jdk/java/sql/util/StubPreparedStatement.java similarity index 99% rename from test/jdk/java/sql/testng/util/StubPreparedStatement.java rename to test/jdk/java/sql/util/StubPreparedStatement.java index a3b95a65f4e..fdb90df4797 100644 --- a/test/jdk/java/sql/testng/util/StubPreparedStatement.java +++ b/test/jdk/java/sql/util/StubPreparedStatement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 diff --git a/test/jdk/java/sql/testng/util/StubStatement.java b/test/jdk/java/sql/util/StubStatement.java similarity index 99% rename from test/jdk/java/sql/testng/util/StubStatement.java rename to test/jdk/java/sql/util/StubStatement.java index c8f6a3ac071..00ed6268856 100644 --- a/test/jdk/java/sql/testng/util/StubStatement.java +++ b/test/jdk/java/sql/util/StubStatement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 diff --git a/test/jdk/java/time/tck/java/time/TCKOffsetTime.java b/test/jdk/java/time/tck/java/time/TCKOffsetTime.java index 7ea9504edbc..8aab9f53977 100644 --- a/test/jdk/java/time/tck/java/time/TCKOffsetTime.java +++ b/test/jdk/java/time/tck/java/time/TCKOffsetTime.java @@ -421,7 +421,6 @@ public class TCKOffsetTime extends AbstractDateTimeTest { {"00;00"}, {"12-00"}, {"-01:00"}, - {"00:00:00-09"}, {"00:00:00,09"}, {"00:00:abs"}, {"11"}, @@ -436,6 +435,11 @@ public class TCKOffsetTime extends AbstractDateTimeTest { Assertions.assertThrows(DateTimeParseException.class, () -> OffsetTime.parse(unparsable)); } + @Test + public void factory_parse_hourOnlyOffset() { + Assertions.assertDoesNotThrow(() -> OffsetTime.parse("00:00:00-09")); + } + //-----------------------------------------------------------------------s @Test public void factory_parse_illegalHour() { diff --git a/test/jdk/java/time/test/java/time/format/TestDateTimeFormatter.java b/test/jdk/java/time/test/java/time/format/TestDateTimeFormatter.java index 2ccc0da8f58..d6c73f6bc74 100644 --- a/test/jdk/java/time/test/java/time/format/TestDateTimeFormatter.java +++ b/test/jdk/java/time/test/java/time/format/TestDateTimeFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026, 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 @@ -59,6 +59,7 @@ */ package test.java.time.format; +import static java.time.format.DateTimeFormatter.*; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -93,15 +94,19 @@ import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.util.Locale; import java.util.function.Function; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; + /** * Test DateTimeFormatter. - * @bug 8085887 8293146 + * @bug 8085887 8293146 8210336 */ @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class TestDateTimeFormatter { @@ -333,4 +338,49 @@ public class TestDateTimeFormatter { assertThrows(DateTimeException.class, () -> LocalDate.parse(weekDate, f)); } } + + private static Stream data_iso_short_offset_parse() { + return Stream.of( + Arguments.of("20260123-01", BASIC_ISO_DATE, "20260123-0100"), + Arguments.of("20260123+00", BASIC_ISO_DATE, "20260123Z"), + Arguments.of("20260123-00", BASIC_ISO_DATE, "20260123Z"), + Arguments.of("2026-01-23-01", ISO_DATE, "2026-01-23-01:00"), + Arguments.of("2026-01-23+00", ISO_DATE, "2026-01-23Z"), + Arguments.of("2026-01-23-00", ISO_DATE, "2026-01-23Z"), + Arguments.of("2026-01-23T11:30:59-01", ISO_DATE_TIME, "2026-01-23T11:30:59-01:00"), + Arguments.of("2026-01-23T11:30:59+00", ISO_DATE_TIME, "2026-01-23T11:30:59Z"), + Arguments.of("2026-01-23T11:30:59-00", ISO_DATE_TIME, "2026-01-23T11:30:59Z"), + Arguments.of("11:30:59-01", ISO_TIME, "11:30:59-01:00"), + Arguments.of("11:30:59+00", ISO_TIME, "11:30:59Z"), + Arguments.of("11:30:59-00", ISO_TIME, "11:30:59Z"), + Arguments.of("2026-01-23-01", ISO_OFFSET_DATE, "2026-01-23-01:00"), + Arguments.of("2026-01-23+00", ISO_OFFSET_DATE, "2026-01-23Z"), + Arguments.of("2026-01-23-00", ISO_OFFSET_DATE, "2026-01-23Z"), + Arguments.of("2026-01-23T11:30:59-01", ISO_OFFSET_DATE_TIME, "2026-01-23T11:30:59-01:00"), + Arguments.of("2026-01-23T11:30:59+00", ISO_OFFSET_DATE_TIME, "2026-01-23T11:30:59Z"), + Arguments.of("2026-01-23T11:30:59-00", ISO_OFFSET_DATE_TIME, "2026-01-23T11:30:59Z"), + Arguments.of("11:30:59-01", ISO_OFFSET_TIME, "11:30:59-01:00"), + Arguments.of("11:30:59+00", ISO_OFFSET_TIME, "11:30:59Z"), + Arguments.of("11:30:59-00", ISO_OFFSET_TIME, "11:30:59Z"), + Arguments.of("2026-023-01", ISO_ORDINAL_DATE, "2026-023-01:00"), + Arguments.of("2026-023+00", ISO_ORDINAL_DATE, "2026-023Z"), + Arguments.of("2026-023-00", ISO_ORDINAL_DATE, "2026-023Z"), + Arguments.of("2026-W04-5-01", ISO_WEEK_DATE, "2026-W04-5-01:00"), + Arguments.of("2026-W04-5+00", ISO_WEEK_DATE, "2026-W04-5Z"), + Arguments.of("2026-W04-5-00", ISO_WEEK_DATE, "2026-W04-5Z"), + Arguments.of("2026-01-23T11:30:59-01", ISO_ZONED_DATE_TIME, "2026-01-23T11:30:59-01:00"), + Arguments.of("2026-01-23T11:30:59+00", ISO_ZONED_DATE_TIME, "2026-01-23T11:30:59Z"), + Arguments.of("2026-01-23T11:30:59-00", ISO_ZONED_DATE_TIME, "2026-01-23T11:30:59Z"), + Arguments.of("2026-01-23T11:30:59-01", ISO_INSTANT, "2026-01-23T12:30:59Z"), + Arguments.of("2026-01-23T11:30:59+00", ISO_INSTANT, "2026-01-23T11:30:59Z"), + Arguments.of("2026-01-23T11:30:59-00", ISO_INSTANT, "2026-01-23T11:30:59Z") + ); + } + + // Checks if predefined ISO formatters can parse hour-only offsets + @ParameterizedTest + @MethodSource("data_iso_short_offset_parse") + public void test_iso_short_offset_parse(String text, DateTimeFormatter formatter, String expected) { + assertEquals(expected, formatter.format(formatter.parse(text))); + } } diff --git a/test/jdk/javax/sql/testng/TEST.properties b/test/jdk/javax/sql/TEST.properties similarity index 68% rename from test/jdk/javax/sql/testng/TEST.properties rename to test/jdk/javax/sql/TEST.properties index cb97c160507..d9ed805344a 100644 --- a/test/jdk/javax/sql/testng/TEST.properties +++ b/test/jdk/javax/sql/TEST.properties @@ -1,7 +1,7 @@ -# JDBC unit tests uses TestNG -TestNG.dirs= . +# JDBC unit tests uses JUnit +JUnit.dirs= . othervm.dirs= . -lib.dirs = /java/sql/testng +lib.dirs = /java/sql/util modules = java.sql.rowset/com.sun.rowset \ java.sql.rowset/com.sun.rowset.internal \ java.sql.rowset/com.sun.rowset.providers diff --git a/test/jdk/javax/sql/testng/jars/badFactory/META-INF/services/javax.sql.rowset.RowSetFactory b/test/jdk/javax/sql/jars/badFactory/META-INF/services/javax.sql.rowset.RowSetFactory similarity index 100% rename from test/jdk/javax/sql/testng/jars/badFactory/META-INF/services/javax.sql.rowset.RowSetFactory rename to test/jdk/javax/sql/jars/badFactory/META-INF/services/javax.sql.rowset.RowSetFactory diff --git a/test/jdk/javax/sql/testng/jars/goodFactory/META-INF/services/javax.sql.rowset.RowSetFactory b/test/jdk/javax/sql/jars/goodFactory/META-INF/services/javax.sql.rowset.RowSetFactory similarity index 100% rename from test/jdk/javax/sql/testng/jars/goodFactory/META-INF/services/javax.sql.rowset.RowSetFactory rename to test/jdk/javax/sql/jars/goodFactory/META-INF/services/javax.sql.rowset.RowSetFactory diff --git a/test/jdk/javax/sql/rowset/TEST.properties b/test/jdk/javax/sql/rowset/TEST.properties deleted file mode 100644 index 6c5a2c7aceb..00000000000 --- a/test/jdk/javax/sql/rowset/TEST.properties +++ /dev/null @@ -1 +0,0 @@ -modules = java.sql.rowset diff --git a/test/jdk/javax/sql/testng/test/rowset/BaseRowSetTests.java b/test/jdk/javax/sql/test/rowset/BaseRowSetTests.java similarity index 83% rename from test/jdk/javax/sql/testng/test/rowset/BaseRowSetTests.java rename to test/jdk/javax/sql/test/rowset/BaseRowSetTests.java index c1d2d9a2ed0..8c47415db39 100644 --- a/test/jdk/javax/sql/testng/test/rowset/BaseRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/BaseRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -40,14 +40,19 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.Calendar; +import java.util.stream.Stream; import javax.sql.RowSet; import javax.sql.rowset.serial.SerialArray; import javax.sql.rowset.serial.SerialBlob; import javax.sql.rowset.serial.SerialClob; import javax.sql.rowset.serial.SerialRef; -import static org.testng.Assert.*; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import util.StubArray; import util.StubBaseRowSet; import util.StubBlob; @@ -67,7 +72,8 @@ public class BaseRowSetTests extends CommonRowSetTests { /* * Create a RowSetListener and validate that notifyCursorMoved is called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0000(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -78,7 +84,8 @@ public class BaseRowSetTests extends CommonRowSetTests { /* * Create a RowSetListener and validate that notifyRowChanged is called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0001(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -89,7 +96,8 @@ public class BaseRowSetTests extends CommonRowSetTests { /* * Create a RowSetListener and validate that notifyRowSetChanged is called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0002(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -101,7 +109,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Create multiple RowSetListeners and validate that notifyRowSetChanged * is called on all listeners */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0003(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); TestRowSetListener rsl2 = new TestRowSetListener(); @@ -116,7 +125,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Create multiple RowSetListeners and validate that notifyRowChanged * is called on all listeners */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0004(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); TestRowSetListener rsl2 = new TestRowSetListener(); @@ -131,7 +141,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Create multiple RowSetListeners and validate that notifyCursorMoved * is called on all listeners */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0005(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); TestRowSetListener rsl2 = new TestRowSetListener(); @@ -146,7 +157,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Create a RowSetListener and validate that notifyRowSetChanged, * notifyRowChanged() and notifyCursorMoved are called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0006(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -163,7 +175,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Create multiple RowSetListeners and validate that notifyRowSetChanged, * notifyRowChanged() and notifyCursorMoved are called on all listeners */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0007(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); TestRowSetListener rsl2 = new TestRowSetListener(); @@ -185,7 +198,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * remove the listener, invoke notifyRowSetChanged again and verify the * listner is not called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0008(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -202,7 +216,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Set the base parameters and validate that the value set is * the correct type and value */ - @Test(dataProvider = "testBaseParameters") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("testBaseParameters") public void baseRowSetTest0009(int pos, Object o) throws Exception { assertTrue(getParam(pos, o).getClass().isInstance(o)); assertTrue(o.equals(getParam(pos, o))); @@ -212,7 +227,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Set the complex parameters and validate that the value set is * the correct type */ - @Test(dataProvider = "testAdvancedParameters") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("testAdvancedParameters") public void baseRowSetTest0010(int pos, Object o) throws Exception { assertTrue(getParam(pos, o).getClass().isInstance(o)); } @@ -220,7 +236,8 @@ public class BaseRowSetTests extends CommonRowSetTests { /* * Validate setNull specifying the supported type values */ - @Test(dataProvider = "jdbcTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("jdbcTypes") public void baseRowSetTest0011(Integer type) throws Exception { brs = new StubBaseRowSet(); brs.setNull(1, type); @@ -231,7 +248,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Validate setNull specifying the supported type values and that * typeName is set internally */ - @Test(dataProvider = "jdbcTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("jdbcTypes") public void baseRowSetTest0012(Integer type) throws Exception { brs = new StubBaseRowSet(); brs.setNull(1, type, "SUPERHERO"); @@ -274,7 +292,8 @@ public class BaseRowSetTests extends CommonRowSetTests { /* * Validate that initParams() initializes the parameters */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0016(StubBaseRowSet rs) throws Exception { rs.setInt(1, 1); rs.initParams(); @@ -285,8 +304,7 @@ public class BaseRowSetTests extends CommonRowSetTests { /* * DataProvider used to set parameters for basic types that are supported */ - @DataProvider(name = "testBaseParameters") - private Object[][] testBaseParameters() throws SQLException { + private Stream testBaseParameters() throws SQLException { Integer aInt = 1; Long aLong = Long.MAX_VALUE; Short aShort = Short.MIN_VALUE; @@ -320,34 +338,32 @@ public class BaseRowSetTests extends CommonRowSetTests { brs.setObject(17, query, Types.CHAR); brs.setObject(18, query, Types.CHAR, 0); - return new Object[][]{ - {1, aInt}, - {2, query}, - {3, aLong}, - {4, aBoolean}, - {5, aShort}, - {6, aDouble}, - {7, bd}, - {8, aFloat}, - {9, aByte}, - {10, aDate}, - {11, aTime}, - {12, aTimeStamp}, - {13, aDate}, - {14, aTime}, - {15, aTimeStamp}, - {16, query}, - {17, query}, - {18, query} - - }; + return Stream.of( + Arguments.of(1, aInt), + Arguments.of(2, query), + Arguments.of(3, aLong), + Arguments.of(4, aBoolean), + Arguments.of(5, aShort), + Arguments.of(6, aDouble), + Arguments.of(7, bd), + Arguments.of(8, aFloat), + Arguments.of(9, aByte), + Arguments.of(10, aDate), + Arguments.of(11, aTime), + Arguments.of(12, aTimeStamp), + Arguments.of(13, aDate), + Arguments.of(14, aTime), + Arguments.of(15, aTimeStamp), + Arguments.of(16, query), + Arguments.of(17, query), + Arguments.of(18, query) + ); } /* * DataProvider used to set advanced parameters for types that are supported */ - @DataProvider(name = "testAdvancedParameters") - private Object[][] testAdvancedParameters() throws SQLException { + private Stream testAdvancedParameters() throws SQLException { byte[] bytes = new byte[10]; Ref aRef = new SerialRef(new StubRef("INTEGER", query)); @@ -367,17 +383,17 @@ public class BaseRowSetTests extends CommonRowSetTests { brs.setUnicodeStream(8, is, query.length()); brs.setCharacterStream(9, rdr, query.length()); - return new Object[][]{ - {1, bytes}, - {2, is}, - {3, aRef}, - {4, aArray}, - {5, aBlob}, - {6, aClob}, - {7, is}, - {8, is}, - {9, rdr} - }; + return Stream.of( + Arguments.of(1, bytes), + Arguments.of(2, is), + Arguments.of(3, aRef), + Arguments.of(4, aArray), + Arguments.of(5, aBlob), + Arguments.of(6, aClob), + Arguments.of(7, is), + Arguments.of(8, is), + Arguments.of(9, rdr) + ); } /* diff --git a/test/jdk/javax/sql/testng/test/rowset/CommonRowSetTests.java b/test/jdk/javax/sql/test/rowset/CommonRowSetTests.java similarity index 67% rename from test/jdk/javax/sql/testng/test/rowset/CommonRowSetTests.java rename to test/jdk/javax/sql/test/rowset/CommonRowSetTests.java index 89492aefed5..5e8163fbf05 100644 --- a/test/jdk/javax/sql/testng/test/rowset/CommonRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/CommonRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -43,17 +43,23 @@ import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; import javax.sql.RowSet; import javax.sql.rowset.BaseRowSet; import javax.sql.rowset.CachedRowSet; import javax.sql.rowset.RowSetFactory; import javax.sql.rowset.RowSetMetaDataImpl; import javax.sql.rowset.RowSetProvider; -import org.testng.Assert; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import util.BaseTest; import util.StubBlob; import util.StubClob; @@ -98,7 +104,7 @@ public abstract class CommonRowSetTests extends BaseTest { try { rsf = RowSetProvider.newFactory(); } catch (SQLException ex) { - Assert.fail(ex.getMessage()); + fail(ex.getMessage()); } } @@ -111,84 +117,76 @@ public abstract class CommonRowSetTests extends BaseTest { * DataProvider used to specify the value to set and check for the * methods for fetch direction */ - @DataProvider(name = "rowSetFetchDirection") - protected Object[][] rowSetFetchDirection() throws Exception { + protected Stream rowSetFetchDirection() throws Exception { RowSet rs = newInstance(); - return new Object[][]{ - {rs, ResultSet.FETCH_FORWARD}, - {rs, ResultSet.FETCH_REVERSE}, - {rs, ResultSet.FETCH_UNKNOWN} - }; + return Stream.of( + Arguments.of(rs, ResultSet.FETCH_FORWARD), + Arguments.of(rs, ResultSet.FETCH_REVERSE), + Arguments.of(rs, ResultSet.FETCH_UNKNOWN) + ); } /* * DataProvider used to specify the value to set and check for the * methods for Cursor Scroll Type */ - @DataProvider(name = "rowSetScrollTypes") - protected Object[][] rowSetScrollTypes() throws Exception { + protected Stream rowSetScrollTypes() throws Exception { RowSet rs = newInstance(); - return new Object[][]{ - {rs, ResultSet.TYPE_FORWARD_ONLY}, - {rs, ResultSet.TYPE_SCROLL_INSENSITIVE}, - {rs, ResultSet.TYPE_SCROLL_SENSITIVE} - }; + return Stream.of( + Arguments.of(rs, ResultSet.TYPE_FORWARD_ONLY), + Arguments.of(rs, ResultSet.TYPE_SCROLL_INSENSITIVE), + Arguments.of(rs, ResultSet.TYPE_SCROLL_SENSITIVE) + ); } /* * DataProvider used to specify the value to set and check for * methods using transaction isolation types */ - @DataProvider(name = "rowSetIsolationTypes") - protected Object[][] rowSetIsolationTypes() throws Exception { + protected Stream rowSetIsolationTypes() throws Exception { RowSet rs = newInstance(); - return new Object[][]{ - {rs, Connection.TRANSACTION_NONE}, - {rs, Connection.TRANSACTION_READ_COMMITTED}, - {rs, Connection.TRANSACTION_READ_UNCOMMITTED}, - {rs, Connection.TRANSACTION_REPEATABLE_READ}, - {rs, Connection.TRANSACTION_SERIALIZABLE} - }; + return Stream.of( + Arguments.of(rs, Connection.TRANSACTION_NONE), + Arguments.of(rs, Connection.TRANSACTION_READ_COMMITTED), + Arguments.of(rs, Connection.TRANSACTION_READ_UNCOMMITTED), + Arguments.of(rs, Connection.TRANSACTION_REPEATABLE_READ), + Arguments.of(rs, Connection.TRANSACTION_SERIALIZABLE) + ); } /* * DataProvider used to specify the value to set and check for the * methods for Concurrency */ - @DataProvider(name = "rowSetConcurrencyTypes") - protected Object[][] rowSetConcurrencyTypes() throws Exception { + protected Stream rowSetConcurrencyTypes() throws Exception { RowSet rs = newInstance(); - return new Object[][]{ - {rs, ResultSet.CONCUR_READ_ONLY}, - {rs, ResultSet.CONCUR_UPDATABLE} - }; + return Stream.of( + Arguments.of(rs, ResultSet.CONCUR_READ_ONLY), + Arguments.of(rs, ResultSet.CONCUR_UPDATABLE) + ); } /* * DataProvider used to specify the value to set and check for * methods using boolean values */ - @DataProvider(name = "rowSetTrueFalse") - protected Object[][] rowSetTrueFalse() throws Exception { + protected Stream rowSetTrueFalse() throws Exception { RowSet rs = newInstance(); - return new Object[][]{ - {rs, true}, - {rs, false} - }; + return Stream.of( + Arguments.of(rs, true), + Arguments.of(rs, false) + ); } /* * DataProvider used to specify the type of RowSet to use. We also must * initialize the RowSet */ - @DataProvider(name = "rowSetType") - protected Object[][] rowSetType() throws Exception { + protected Stream rowSetType() throws Exception { RowSet rs = newInstance(); - return new Object[][]{ - {rs} - }; + return Stream.of(rs); } /* @@ -503,7 +501,8 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that getCommand() returns null by default */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0000(RowSet rs) { assertNull(rs.getCommand()); } @@ -511,7 +510,8 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that getCommand() returns command specified to setCommand */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0001(RowSet rs) throws Exception { rs.setCommand(query); assertTrue(rs.getCommand().equals(query)); @@ -521,7 +521,8 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that getCurrency() returns the correct default value */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0002(RowSet rs) throws Exception { assertTrue(rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE); } @@ -530,7 +531,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getCurrency() returns the correct value * after a call to setConcurrency()) */ - @Test(dataProvider = "rowSetConcurrencyTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetConcurrencyTypes") public void commonRowSetTest0003(RowSet rs, int concurType) throws Exception { rs.setConcurrency(concurType); assertTrue(rs.getConcurrency() == concurType); @@ -539,15 +541,17 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that getCurrency() throws a SQLException for an invalid value */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0004(RowSet rs) throws Exception { - rs.setConcurrency(ResultSet.CLOSE_CURSORS_AT_COMMIT); + assertThrows(SQLException.class, () -> rs.setConcurrency(ResultSet.CLOSE_CURSORS_AT_COMMIT)); } /* * Validate that getDataSourceName() returns null by default */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0005(RowSet rs) throws Exception { assertTrue(rs.getDataSourceName() == null); } @@ -556,7 +560,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getDataSourceName() returns the value specified * by setDataSourceName() and getUrl() returns null */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0006(RowSet rs) throws Exception { rs.setUrl(url); rs.setDataSourceName(dsName); @@ -568,16 +573,20 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that setDataSourceName() throws a SQLException for an empty * String specified for the data source name */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0007(RowSet rs) throws Exception { - String dsname = ""; - rs.setDataSourceName(dsname); + assertThrows(SQLException.class, () -> { + String dsname = ""; + rs.setDataSourceName(dsname); + }); } /* * Validate that getEscapeProcessing() returns false by default */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0008(RowSet rs) throws Exception { assertTrue(rs.getEscapeProcessing()); } @@ -586,7 +595,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getEscapeProcessing() returns value set by * setEscapeProcessing() */ - @Test(dataProvider = "rowSetTrueFalse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetTrueFalse") public void commonRowSetTest0009(RowSet rs, boolean val) throws Exception { rs.setEscapeProcessing(val); assertTrue(rs.getEscapeProcessing() == val); @@ -595,7 +605,8 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that getFetchDirection() returns the correct default value */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0010(RowSet rs) throws Exception { assertTrue(rs.getFetchDirection() == ResultSet.FETCH_FORWARD); } @@ -604,7 +615,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getFetchDirection() returns the value set by * setFetchDirection() */ - @Test(dataProvider = "rowSetFetchDirection") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetFetchDirection") public void commonRowSetTest0011(RowSet rs, int direction) throws Exception { rs.setFetchDirection(direction); assertTrue(rs.getFetchDirection() == direction); @@ -613,26 +625,31 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that setFetchSize() throws a SQLException for an invalid value */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0013(RowSet rs) throws Exception { - rs.setFetchSize(-1); + assertThrows(SQLException.class, () -> rs.setFetchSize(-1)); } /* * Validate that setFetchSize() throws a SQLException for a * value greater than getMaxRows() */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0014(RowSet rs) throws Exception { - rs.setMaxRows(5); - rs.setFetchSize(rs.getMaxRows() + 1); + assertThrows(SQLException.class, () -> { + rs.setMaxRows(5); + rs.setFetchSize(rs.getMaxRows() + 1); + }); } /* * Validate that getFetchSize() returns the correct value after * setFetchSize() has been called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0015(RowSet rs) throws Exception { int maxRows = 150; rs.setFetchSize(0); @@ -647,16 +664,18 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that setMaxFieldSize() throws a SQLException for an invalid value */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0016(RowSet rs) throws Exception { - rs.setMaxFieldSize(-1); + assertThrows(SQLException.class, () -> rs.setMaxFieldSize(-1)); } /* * Validate that getMaxFieldSize() returns the value set by * setMaxFieldSize() */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0017(RowSet rs) throws Exception { rs.setMaxFieldSize(0); assertTrue(rs.getMaxFieldSize() == 0); @@ -670,7 +689,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that isReadOnly() returns value set by * setReadOnly() */ - @Test(dataProvider = "rowSetTrueFalse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetTrueFalse") public void commonRowSetTest0018(RowSet rs, boolean val) throws Exception { rs.setReadOnly(val); assertTrue(rs.isReadOnly() == val); @@ -680,7 +700,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getTransactionIsolation() returns value set by * setTransactionIsolation() */ - @Test(dataProvider = "rowSetIsolationTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetIsolationTypes") public void commonRowSetTest0019(RowSet rs, int val) throws Exception { rs.setTransactionIsolation(val); assertTrue(rs.getTransactionIsolation() == val); @@ -689,7 +710,8 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that getType() returns value set by setType() */ - @Test(dataProvider = "rowSetScrollTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetScrollTypes") public void commonRowSetTest0020(RowSet rs, int val) throws Exception { rs.setType(val); assertTrue(rs.getType() == val); @@ -699,7 +721,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getEscapeProcessing() returns value set by * setEscapeProcessing() */ - @Test(dataProvider = "rowSetTrueFalse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetTrueFalse") public void commonRowSetTest0021(BaseRowSet rs, boolean val) throws Exception { rs.setShowDeleted(val); assertTrue(rs.getShowDeleted() == val); @@ -709,7 +732,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getTypeMap() returns same value set by * setTypeMap() */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0022(RowSet rs) throws Exception { Map> map = new HashMap<>(); map.put("SUPERHERO", Class.forName("util.SuperHero")); @@ -721,7 +745,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getUsername() returns same value set by * setUsername() */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0023(RowSet rs) throws Exception { rs.setUsername(user); assertTrue(rs.getUsername().equals(user)); @@ -731,7 +756,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getPassword() returns same password set by * setPassword() */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0024(RowSet rs) throws Exception { rs.setPassword(password); assertTrue(rs.getPassword().equals(password)); @@ -741,7 +767,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getQueryTimeout() returns same value set by * setQueryTimeout() and that 0 is a valid timeout value */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0025(RowSet rs) throws Exception { int timeout = 0; rs.setQueryTimeout(timeout); @@ -752,7 +779,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getQueryTimeout() returns same value set by * setQueryTimeout() and that 0 is a valid timeout value */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0026(RowSet rs) throws Exception { int timeout = 10000; rs.setQueryTimeout(timeout); @@ -763,9 +791,10 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that setQueryTimeout() throws a SQLException for a timeout * value < 0 */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0027(RowSet rs) throws Exception { - rs.setQueryTimeout(-1); + assertThrows(SQLException.class, () -> rs.setQueryTimeout(-1)); } @@ -773,7 +802,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate addRowSetListener does not throw an Exception when null is * passed as the parameter */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0028(RowSet rs) throws Exception { rs.addRowSetListener(null); } @@ -782,7 +812,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate removeRowSetListener does not throw an Exception when null is * passed as the parameter */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0029(RowSet rs) throws Exception { rs.removeRowSetListener(null); } @@ -790,7 +821,8 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Set two parameters and then validate clearParameters() will clear them */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0030(BaseRowSet rs) throws Exception { rs.setInt(1, 1); rs.setString(2, query); @@ -803,7 +835,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getURL() returns same value set by * setURL() */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0031(RowSet rs) throws Exception { rs.setUrl(url); assertTrue(rs.getUrl().equals(url)); @@ -813,560 +846,614 @@ public abstract class CommonRowSetTests extends BaseTest { * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0100(RowSet rs) throws Exception { - InputStream is = null; - rs.setAsciiStream(1, is); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setAsciiStream(1, is); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0101(RowSet rs) throws Exception { - InputStream is = null; - rs.setAsciiStream("one", is); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setAsciiStream("one", is); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0102(RowSet rs) throws Exception { - InputStream is = null; - rs.setAsciiStream("one", is, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setAsciiStream("one", is, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0103(RowSet rs) throws Exception { - InputStream is = null; - rs.setBinaryStream(1, is); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setBinaryStream(1, is); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0104(RowSet rs) throws Exception { - InputStream is = null; - rs.setBinaryStream("one", is); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setBinaryStream("one", is); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0105(RowSet rs) throws Exception { - InputStream is = null; - rs.setBinaryStream("one", is, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setBinaryStream("one", is, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0106(RowSet rs) throws Exception { - rs.setBigDecimal("one", BigDecimal.ONE); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setBigDecimal("one", BigDecimal.ONE)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0107(RowSet rs) throws Exception { - InputStream is = null; - rs.setBlob(1, is); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setBlob(1, is); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0108(RowSet rs) throws Exception { - InputStream is = null; - rs.setBlob("one", is); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setBlob("one", is); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0109(RowSet rs) throws Exception { - InputStream is = null; - rs.setBlob("one", is, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setBlob("one", is, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0110(RowSet rs) throws Exception { - rs.setBlob("one", new StubBlob()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setBlob("one", new StubBlob())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0111(RowSet rs) throws Exception { - rs.setBoolean("one", true); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setBoolean("one", true)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0112(RowSet rs) throws Exception { - byte b = 1; - rs.setByte("one", b); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + byte b = 1; + rs.setByte("one", b); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0113(RowSet rs) throws Exception { - byte b = 1; - rs.setBytes("one", new byte[10]); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + byte b = 1; + rs.setBytes("one", new byte[10]); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0114(RowSet rs) throws Exception { - Reader rdr = null; - rs.setCharacterStream("one", rdr, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setCharacterStream("one", rdr, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0115(RowSet rs) throws Exception { - Reader rdr = null; - rs.setCharacterStream("one", rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setCharacterStream("one", rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0116(RowSet rs) throws Exception { - Reader rdr = null; - rs.setCharacterStream(1, rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setCharacterStream(1, rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0117(RowSet rs) throws Exception { - Reader rdr = null; - rs.setClob(1, rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setClob(1, rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0118(RowSet rs) throws Exception { - Reader rdr = null; - rs.setClob("one", rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setClob("one", rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0119(RowSet rs) throws Exception { - Reader rdr = null; - rs.setClob("one", rdr, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setClob("one", rdr, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0120(RowSet rs) throws Exception { - rs.setClob("one", new StubClob()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setClob("one", new StubClob())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0121(RowSet rs) throws Exception { - rs.setDate("one", Date.valueOf(LocalDate.now())); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setDate("one", Date.valueOf(LocalDate.now()))); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0122(RowSet rs) throws Exception { - rs.setDate("one", Date.valueOf(LocalDate.now()), - Calendar.getInstance()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setDate("one", Date.valueOf(LocalDate.now()), + Calendar.getInstance())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0123(RowSet rs) throws Exception { - rs.setTime("one", Time.valueOf(LocalTime.now())); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setTime("one", Time.valueOf(LocalTime.now()))); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0124(RowSet rs) throws Exception { - rs.setTime("one", Time.valueOf(LocalTime.now()), - Calendar.getInstance()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setTime("one", Time.valueOf(LocalTime.now()), + Calendar.getInstance())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0125(RowSet rs) throws Exception { - rs.setTimestamp("one", Timestamp.valueOf(LocalDateTime.now())); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setTimestamp("one", Timestamp.valueOf(LocalDateTime.now()))); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0126(RowSet rs) throws Exception { - rs.setTimestamp("one", Timestamp.valueOf(LocalDateTime.now()), - Calendar.getInstance()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setTimestamp("one", Timestamp.valueOf(LocalDateTime.now()), + Calendar.getInstance())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0127(RowSet rs) throws Exception { - rs.setDouble("one", 2.0d); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setDouble("one", 2.0d)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0128(RowSet rs) throws Exception { - rs.setFloat("one", 2.0f); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setFloat("one", 2.0f)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0129(RowSet rs) throws Exception { - rs.setInt("one", 21); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setInt("one", 21)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0130(RowSet rs) throws Exception { - rs.setLong("one", 21l); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setLong("one", 21l)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0131(RowSet rs) throws Exception { - Reader rdr = null; - rs.setNCharacterStream("one", rdr, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setNCharacterStream("one", rdr, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0132(RowSet rs) throws Exception { - Reader rdr = null; - rs.setNCharacterStream("one", rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setNCharacterStream("one", rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0133(RowSet rs) throws Exception { - Reader rdr = null; - rs.setNCharacterStream(1, rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setNCharacterStream(1, rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0134(RowSet rs) throws Exception { - Reader rdr = null; - rs.setNCharacterStream(1, rdr, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setNCharacterStream(1, rdr, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0135(RowSet rs) throws Exception { - Reader rdr = null; - rs.setClob("one", rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setClob("one", rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0136(RowSet rs) throws Exception { - Reader rdr = null; - rs.setClob("one", rdr, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setClob("one", rdr, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0137(RowSet rs) throws Exception { - rs.setNClob("one", new StubNClob()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setNClob("one", new StubNClob())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0138(RowSet rs) throws Exception { - Reader rdr = null; - rs.setNClob(1, rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setNClob(1, rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0139(RowSet rs) throws Exception { - Reader rdr = null; - rs.setNClob(1, rdr, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setNClob(1, rdr, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0140(RowSet rs) throws Exception { - rs.setNClob(1, new StubNClob()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setNClob(1, new StubNClob())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0141(RowSet rs) throws Exception { - rs.setNString(1, query); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setNString(1, query)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0142(RowSet rs) throws Exception { - rs.setNull("one", Types.INTEGER); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setNull("one", Types.INTEGER)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0143(RowSet rs) throws Exception { - rs.setNull("one", Types.INTEGER, "my.type"); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setNull("one", Types.INTEGER, "my.type")); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0144(RowSet rs) throws Exception { - rs.setObject("one", query, Types.VARCHAR); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setObject("one", query, Types.VARCHAR)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0145(RowSet rs) throws Exception { - rs.setObject("one", query, Types.VARCHAR, 0); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setObject("one", query, Types.VARCHAR, 0)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0146(RowSet rs) throws Exception { - rs.setObject("one", query); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setObject("one", query)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0147(RowSet rs) throws Exception { - RowId aRowid = null; - rs.setRowId("one", aRowid); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + RowId aRowid = null; + rs.setRowId("one", aRowid); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0148(RowSet rs) throws Exception { - rs.setSQLXML("one", new StubSQLXML()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setSQLXML("one", new StubSQLXML())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0149(RowSet rs) throws Exception { - rs.setSQLXML(1, new StubSQLXML()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setSQLXML(1, new StubSQLXML())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0150(RowSet rs) throws Exception { - rs.setNString(1, query); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setNString(1, query)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0151(RowSet rs) throws Exception { - rs.setNString("one", query); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setNString("one", query)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0152(RowSet rs) throws Exception { - short val = 21; - rs.setShort("one", val); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + short val = 21; + rs.setShort("one", val); + }); } } diff --git a/test/jdk/javax/sql/testng/test/rowset/RowSetFactoryTests.java b/test/jdk/javax/sql/test/rowset/RowSetFactoryTests.java similarity index 80% rename from test/jdk/javax/sql/testng/test/rowset/RowSetFactoryTests.java rename to test/jdk/javax/sql/test/rowset/RowSetFactoryTests.java index 72a4341c7aa..480033afc2b 100644 --- a/test/jdk/javax/sql/testng/test/rowset/RowSetFactoryTests.java +++ b/test/jdk/javax/sql/test/rowset/RowSetFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -23,11 +23,15 @@ package test.rowset; import java.sql.SQLException; +import java.util.stream.Stream; import javax.sql.rowset.RowSetFactory; import javax.sql.rowset.RowSetProvider; -import static org.testng.Assert.*; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import util.BaseTest; public class RowSetFactoryTests extends BaseTest { @@ -50,7 +54,8 @@ public class RowSetFactoryTests extends BaseTest { * Validate that the RowSetFactory returned by RowSetProvider.newFactory() * returns the correct RowSet implementations */ - @Test(dataProvider = "RowSetValues", enabled = true) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("RowSetValues") public void test(RowSetFactory rsf, String impl) throws SQLException { validateRowSetImpl(rsf, impl); } @@ -98,22 +103,20 @@ public class RowSetFactoryTests extends BaseTest { * DataProvider used to provide the RowSetFactory and the RowSet * implementation that should be returned */ - @DataProvider(name = "RowSetValues") - private Object[][] RowSetValues() throws SQLException { + private Stream RowSetValues() throws SQLException { RowSetFactory rsf = RowSetProvider.newFactory(); RowSetFactory rsf1 = RowSetProvider.newFactory(STUB_FACTORY_CLASSNAME, null); - return new Object[][]{ - {rsf, DEFAULT_CACHEDROWSET_CLASSNAME}, - {rsf, DEFAULT_FILTEREDROWSET_CLASSNAME}, - {rsf, DEFAULT_JDBCROWSET_CLASSNAME}, - {rsf, DEFAULT_JOINROWSET_CLASSNAME}, - {rsf, DEFAULT_WEBROWSET_CLASSNAME}, - {rsf1, STUB_CACHEDROWSET_CLASSNAME}, - {rsf1, STUB_FILTEREDROWSET_CLASSNAME}, - {rsf1, STUB_JDBCROWSET_CLASSNAME}, - {rsf1, STUB_JOINROWSET_CLASSNAME}, - {rsf1, STUB_WEBROWSET_CLASSNAME} - - }; + return Stream.of( + Arguments.of(rsf, DEFAULT_CACHEDROWSET_CLASSNAME), + Arguments.of(rsf, DEFAULT_FILTEREDROWSET_CLASSNAME), + Arguments.of(rsf, DEFAULT_JDBCROWSET_CLASSNAME), + Arguments.of(rsf, DEFAULT_JOINROWSET_CLASSNAME), + Arguments.of(rsf, DEFAULT_WEBROWSET_CLASSNAME), + Arguments.of(rsf1, STUB_CACHEDROWSET_CLASSNAME), + Arguments.of(rsf1, STUB_FILTEREDROWSET_CLASSNAME), + Arguments.of(rsf1, STUB_JDBCROWSET_CLASSNAME), + Arguments.of(rsf1, STUB_JOINROWSET_CLASSNAME), + Arguments.of(rsf1, STUB_WEBROWSET_CLASSNAME) + ); } } diff --git a/test/jdk/javax/sql/testng/test/rowset/RowSetMetaDataTests.java b/test/jdk/javax/sql/test/rowset/RowSetMetaDataTests.java similarity index 55% rename from test/jdk/javax/sql/testng/test/rowset/RowSetMetaDataTests.java rename to test/jdk/javax/sql/test/rowset/RowSetMetaDataTests.java index 8a944a8bbb1..59dc4f43033 100644 --- a/test/jdk/javax/sql/testng/test/rowset/RowSetMetaDataTests.java +++ b/test/jdk/javax/sql/test/rowset/RowSetMetaDataTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -25,12 +25,19 @@ package test.rowset; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Types; +import java.util.stream.IntStream; +import java.util.stream.Stream; import javax.sql.RowSetMetaData; import javax.sql.rowset.RowSetMetaDataImpl; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import org.junit.jupiter.params.provider.ValueSource; import util.BaseTest; public class RowSetMetaDataTests extends BaseTest { @@ -40,7 +47,7 @@ public class RowSetMetaDataTests extends BaseTest { // Instance to be used within the tests private RowSetMetaDataImpl rsmd; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { rsmd = new RowSetMetaDataImpl(); rsmd.setColumnCount(MAX_COLUMNS); @@ -49,325 +56,325 @@ public class RowSetMetaDataTests extends BaseTest { /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test(Integer col) throws Exception { - rsmd.getCatalogName(col); + assertThrows(SQLException.class, () -> rsmd.getCatalogName(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test01(Integer col) throws Exception { - rsmd.getColumnClassName(col); + assertThrows(SQLException.class, () -> rsmd.getColumnClassName(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test02(Integer col) throws Exception { - rsmd.getColumnDisplaySize(col); + assertThrows(SQLException.class, () -> rsmd.getColumnDisplaySize(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test03(Integer col) throws Exception { - rsmd.getColumnLabel(col); + assertThrows(SQLException.class, () -> rsmd.getColumnLabel(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test04(Integer col) throws Exception { - rsmd.getColumnName(col); + assertThrows(SQLException.class, () -> rsmd.getColumnName(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test05(Integer col) throws Exception { - rsmd.getColumnType(col); + assertThrows(SQLException.class, () -> rsmd.getColumnType(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test06(Integer col) throws Exception { - rsmd.getColumnTypeName(col); + assertThrows(SQLException.class, () -> rsmd.getColumnTypeName(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test07(Integer col) throws Exception { - rsmd.getPrecision(col); + assertThrows(SQLException.class, () -> rsmd.getPrecision(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test08(Integer col) throws Exception { - rsmd.getScale(col); + assertThrows(SQLException.class, () -> rsmd.getScale(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test09(Integer col) throws Exception { - rsmd.getSchemaName(col); + assertThrows(SQLException.class, () -> rsmd.getSchemaName(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test10(Integer col) throws Exception { - rsmd.getTableName(col); + assertThrows(SQLException.class, () -> rsmd.getTableName(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test11(Integer col) throws Exception { - rsmd.isAutoIncrement(col); + assertThrows(SQLException.class, () -> rsmd.isAutoIncrement(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test12(Integer col) throws Exception { - rsmd.isCaseSensitive(col); + assertThrows(SQLException.class, () -> rsmd.isCaseSensitive(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test13(Integer col) throws Exception { - rsmd.isCurrency(col); + assertThrows(SQLException.class, () -> rsmd.isCurrency(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test14(Integer col) throws Exception { - rsmd.isDefinitelyWritable(col); + assertThrows(SQLException.class, () -> rsmd.isDefinitelyWritable(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test15(Integer col) throws Exception { - rsmd.isNullable(col); + assertThrows(SQLException.class, () -> rsmd.isNullable(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test16(Integer col) throws Exception { - rsmd.isReadOnly(col); + assertThrows(SQLException.class, () -> rsmd.isReadOnly(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test17(Integer col) throws Exception { - rsmd.isSearchable(col); + assertThrows(SQLException.class, () -> rsmd.isSearchable(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test18(Integer col) throws Exception { - rsmd.isSigned(col); + assertThrows(SQLException.class, () -> rsmd.isSigned(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test19(Integer col) throws Exception { - rsmd.isWritable(col); + assertThrows(SQLException.class, () -> rsmd.isWritable(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test20(Integer col) throws Exception { - rsmd.setAutoIncrement(col, true); + assertThrows(SQLException.class, () -> rsmd.setAutoIncrement(col, true)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test21(Integer col) throws Exception { - rsmd.setCaseSensitive(col, true); + assertThrows(SQLException.class, () -> rsmd.setCaseSensitive(col, true)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test22(Integer col) throws Exception { - rsmd.setCatalogName(col, null); + assertThrows(SQLException.class, () -> rsmd.setCatalogName(col, null)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test23(Integer col) throws Exception { - rsmd.setColumnDisplaySize(col, 5); + assertThrows(SQLException.class, () -> rsmd.setColumnDisplaySize(col, 5)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test24(Integer col) throws Exception { - rsmd.setColumnLabel(col, "label"); + assertThrows(SQLException.class, () -> rsmd.setColumnLabel(col, "label")); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test25(Integer col) throws Exception { - rsmd.setColumnName(col, "F1"); + assertThrows(SQLException.class, () -> rsmd.setColumnName(col, "F1")); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test26(Integer col) throws Exception { - rsmd.setColumnType(col, Types.CHAR); + assertThrows(SQLException.class, () -> rsmd.setColumnType(col, Types.CHAR)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test27(Integer col) throws Exception { - rsmd.setColumnTypeName(col, "F1"); + assertThrows(SQLException.class, () -> rsmd.setColumnTypeName(col, "F1")); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test28(Integer col) throws Exception { - rsmd.setCurrency(col, true); + assertThrows(SQLException.class, () -> rsmd.setCurrency(col, true)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test29(Integer col) throws Exception { - rsmd.setNullable(col, ResultSetMetaData.columnNoNulls); + assertThrows(SQLException.class, () -> rsmd.setNullable(col, ResultSetMetaData.columnNoNulls)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test30(Integer col) throws Exception { - rsmd.setPrecision(col, 2); + assertThrows(SQLException.class, () -> rsmd.setPrecision(col, 2)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test31(Integer col) throws Exception { - rsmd.setScale(col, 2); + assertThrows(SQLException.class, () -> rsmd.setScale(col, 2)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test32(Integer col) throws Exception { - rsmd.setSchemaName(col, "Gotham"); + assertThrows(SQLException.class, () -> rsmd.setSchemaName(col, "Gotham")); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test33(Integer col) throws Exception { - rsmd.setSearchable(col, false); + assertThrows(SQLException.class, () -> rsmd.setSearchable(col, false)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test34(Integer col) throws Exception { - rsmd.setSigned(col, false); + assertThrows(SQLException.class, () -> rsmd.setSigned(col, false)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test35(Integer col) throws Exception { - rsmd.setTableName(col, "SUPERHEROS"); + assertThrows(SQLException.class, () -> rsmd.setTableName(col, "SUPERHEROS")); } /* @@ -375,7 +382,8 @@ public class RowSetMetaDataTests extends BaseTest { * Note: Once setColumnClassName is added to RowSetMetaData, this * method will need to change. */ - @Test(dataProvider = "columnClassNames") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("columnClassNames") public void test36(Integer type, String name) throws Exception { rsmd.setColumnType(1, type); assertTrue(rsmd.getColumnClassName(1).equals(name)); @@ -385,7 +393,8 @@ public class RowSetMetaDataTests extends BaseTest { * Validate that all of the methods are accessible and the correct value * is returned for each column */ - @Test(dataProvider = "columnRanges") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("columnRanges") public void test37(Integer col) throws Exception { rsmd.setAutoIncrement(col, true); assertTrue(rsmd.isAutoIncrement(col)); @@ -429,7 +438,8 @@ public class RowSetMetaDataTests extends BaseTest { /* * Validate that the proper values are accepted by setNullable */ - @Test(dataProvider = "validSetNullableValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validSetNullableValues") public void test38(Integer val) throws Exception { rsmd.setNullable(1, val); } @@ -437,7 +447,8 @@ public class RowSetMetaDataTests extends BaseTest { /* * Validate that the correct type is returned for the column */ - @Test(dataProvider = "jdbcTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("jdbcTypes") public void test39(Integer type) throws Exception { rsmd.setColumnType(1, type); assertTrue(type == rsmd.getColumnType(1)); @@ -446,7 +457,8 @@ public class RowSetMetaDataTests extends BaseTest { /* * Validate that the correct value is returned from the isXXX methods */ - @Test(dataProvider = "trueFalse") + @ParameterizedTest(autoCloseArguments = false) + @ValueSource(booleans = {true, false}) public void test40(Boolean b) throws Exception { rsmd.setAutoIncrement(1, b); rsmd.setCaseSensitive(1, b); @@ -483,73 +495,62 @@ public class RowSetMetaDataTests extends BaseTest { * to validate that an IllegalArgumentException will be thrown from the * valueOf method */ - @DataProvider(name = "validSetNullableValues") - private Object[][] validSetNullableValues() { - return new Object[][]{ - {ResultSetMetaData.columnNoNulls}, - {ResultSetMetaData.columnNullable}, - {ResultSetMetaData.columnNullableUnknown} - }; + private Stream validSetNullableValues() { + return Stream.of( + ResultSetMetaData.columnNoNulls, + ResultSetMetaData.columnNullable, + ResultSetMetaData.columnNullableUnknown + ); } /* * DataProvider used to provide column indexes that are out of range so that * SQLException is thrown */ - @DataProvider(name = "invalidColumnRanges") - private Object[][] invalidColumnRanges() { - return new Object[][]{ - {-1}, - {0}, - {MAX_COLUMNS + 1} - }; + private Stream invalidColumnRanges() { + return Stream.of( + -1, + 0, + MAX_COLUMNS + 1 + ); } /* * DataProvider used to provide the valid column ranges for the * RowSetMetaDataImpl object */ - @DataProvider(name = "columnRanges") - private Object[][] columnRanges() { - Object[][] o = new Object[MAX_COLUMNS][1]; - for (int i = 1; i <= MAX_COLUMNS; i++) { - o[i - 1][0] = i; - } - return o; + private Stream columnRanges() { + return IntStream.rangeClosed(1, MAX_COLUMNS).boxed(); } /* * DataProvider used to specify the value to set via setColumnType and * the expected value to be returned from getColumnClassName */ - @DataProvider(name = "columnClassNames") - private Object[][] columnClassNames() { - return new Object[][]{ - {Types.CHAR, "java.lang.String"}, - {Types.NCHAR, "java.lang.String"}, - {Types.VARCHAR, "java.lang.String"}, - {Types.NVARCHAR, "java.lang.String"}, - {Types.LONGVARCHAR, "java.lang.String"}, - {Types.LONGNVARCHAR, "java.lang.String"}, - {Types.NUMERIC, "java.math.BigDecimal"}, - {Types.DECIMAL, "java.math.BigDecimal"}, - {Types.BIT, "java.lang.Boolean"}, - {Types.TINYINT, "java.lang.Byte"}, - {Types.SMALLINT, "java.lang.Short"}, - {Types.INTEGER, "java.lang.Integer"}, - {Types.FLOAT, "java.lang.Double"}, - {Types.DOUBLE, "java.lang.Double"}, - {Types.BINARY, "byte[]"}, - {Types.VARBINARY, "byte[]"}, - {Types.LONGVARBINARY, "byte[]"}, - {Types.DATE, "java.sql.Date"}, - {Types.TIME, "java.sql.Time"}, - {Types.TIMESTAMP, "java.sql.Timestamp"}, - {Types.CLOB, "java.sql.Clob"}, - {Types.BLOB, "java.sql.Blob"} - - }; - + private Stream columnClassNames() { + return Stream.of( + Arguments.of(Types.CHAR, "java.lang.String"), + Arguments.of(Types.NCHAR, "java.lang.String"), + Arguments.of(Types.VARCHAR, "java.lang.String"), + Arguments.of(Types.NVARCHAR, "java.lang.String"), + Arguments.of(Types.LONGVARCHAR, "java.lang.String"), + Arguments.of(Types.LONGNVARCHAR, "java.lang.String"), + Arguments.of(Types.NUMERIC, "java.math.BigDecimal"), + Arguments.of(Types.DECIMAL, "java.math.BigDecimal"), + Arguments.of(Types.BIT, "java.lang.Boolean"), + Arguments.of(Types.TINYINT, "java.lang.Byte"), + Arguments.of(Types.SMALLINT, "java.lang.Short"), + Arguments.of(Types.INTEGER, "java.lang.Integer"), + Arguments.of(Types.FLOAT, "java.lang.Double"), + Arguments.of(Types.DOUBLE, "java.lang.Double"), + Arguments.of(Types.BINARY, "byte[]"), + Arguments.of(Types.VARBINARY, "byte[]"), + Arguments.of(Types.LONGVARBINARY, "byte[]"), + Arguments.of(Types.DATE, "java.sql.Date"), + Arguments.of(Types.TIME, "java.sql.Time"), + Arguments.of(Types.TIMESTAMP, "java.sql.Timestamp"), + Arguments.of(Types.CLOB, "java.sql.Clob"), + Arguments.of(Types.BLOB, "java.sql.Blob") + ); } - } diff --git a/test/jdk/javax/sql/testng/test/rowset/RowSetProviderTests.java b/test/jdk/javax/sql/test/rowset/RowSetProviderTests.java similarity index 79% rename from test/jdk/javax/sql/testng/test/rowset/RowSetProviderTests.java rename to test/jdk/javax/sql/test/rowset/RowSetProviderTests.java index 47a5cdb1eb0..25422cf0950 100644 --- a/test/jdk/javax/sql/testng/test/rowset/RowSetProviderTests.java +++ b/test/jdk/javax/sql/test/rowset/RowSetProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -27,14 +27,19 @@ import java.io.File; import java.net.URL; import java.net.URLClassLoader; import java.sql.SQLException; +import java.util.stream.Stream; import javax.sql.rowset.RowSetFactory; import javax.sql.rowset.RowSetProvider; -import static org.testng.Assert.*; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import util.BaseTest; import util.StubRowSetFactory; @@ -57,7 +62,7 @@ public class RowSetProviderTests extends BaseTest { * Save off the original property value for javax.sql.rowset.RowSetFactory, * original classloader and define the path to the jars directory */ - @BeforeClass + @BeforeAll public static void setUpClass() throws Exception { origFactoryProperty = System.getProperty("javax.sql.rowset.RowSetFactory"); cl = Thread.currentThread().getContextClassLoader(); @@ -68,7 +73,7 @@ public class RowSetProviderTests extends BaseTest { /* * Install the original javax.sql.rowset.RowSetFactory property value */ - @AfterClass + @AfterAll public static void tearDownClass() throws Exception { if (origFactoryProperty != null) { System.setProperty("javax.sql.rowset.RowSetFactory", @@ -80,7 +85,7 @@ public class RowSetProviderTests extends BaseTest { * Clear the javax.sql.rowset.RowSetFactory property value and * reset the classloader to its original value */ - @AfterMethod + @AfterEach public void tearDownMethod() throws Exception { System.clearProperty("javax.sql.rowset.RowSetFactory"); Thread.currentThread().setContextClassLoader(cl); @@ -89,7 +94,8 @@ public class RowSetProviderTests extends BaseTest { /* * Validate that the correct RowSetFactory is returned by newFactory(). */ - @Test(dataProvider = "RowSetFactoryValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("RowSetFactoryValues") public void test(RowSetFactory rsf, String impl) throws SQLException { validateProvider(rsf, impl); } @@ -109,7 +115,7 @@ public class RowSetProviderTests extends BaseTest { * Validate that the correct RowSetFactory is returned by newFactory() * when specified by the javax.sql.rowset.RowSetFactory property. */ - @Test(enabled = true) + @Test public void test02() throws SQLException { System.setProperty("javax.sql.rowset.RowSetFactory", STUB_FACTORY_CLASSNAME); validateProvider(RowSetProvider.newFactory(), STUB_FACTORY_CLASSNAME); @@ -120,11 +126,13 @@ public class RowSetProviderTests extends BaseTest { * when specified RowSetFactory specified by the * javax.sql.rowset.RowSetFactory property is not valid. */ - @Test(expectedExceptions = SQLException.class) + @Test public void test03() throws SQLException { - System.setProperty("javax.sql.rowset.RowSetFactory", - "invalid.RowSetFactoryImpl"); - RowSetFactory rsf = RowSetProvider.newFactory(); + assertThrows(SQLException.class, () -> { + System.setProperty("javax.sql.rowset.RowSetFactory", + "invalid.RowSetFactoryImpl"); + RowSetFactory rsf = RowSetProvider.newFactory(); + }); } /* @@ -144,13 +152,15 @@ public class RowSetProviderTests extends BaseTest { * Validate that a SQLException is thrown by newFactory() if the default * RowSetFactory specified by the ServiceLoader API is not valid */ - @Test(expectedExceptions = SQLException.class) + @Test public void test05() throws Exception { - File f = new File(jarPath + "badFactory"); - URLClassLoader loader = new URLClassLoader(new URL[]{ - new URL(f.toURI().toString())}, getClass().getClassLoader()); - Thread.currentThread().setContextClassLoader(loader); - RowSetProvider.newFactory(); + assertThrows(SQLException.class, () -> { + File f = new File(jarPath + "badFactory"); + URLClassLoader loader = new URLClassLoader(new URL[]{ + new URL(f.toURI().toString())}, getClass().getClassLoader()); + Thread.currentThread().setContextClassLoader(loader); + RowSetProvider.newFactory(); + }); } /* @@ -174,16 +184,15 @@ public class RowSetProviderTests extends BaseTest { * DataProvider used to provide a RowSetFactory and the expected * RowSetFactory implementation that should be returned */ - @DataProvider(name = "RowSetFactoryValues") - private Object[][] RowSetFactoryValues() throws SQLException { + private Stream RowSetFactoryValues() throws SQLException { RowSetFactory rsf = RowSetProvider.newFactory(); RowSetFactory rsf1 = RowSetProvider.newFactory(STUB_FACTORY_CLASSNAME, null); RowSetFactory rsf2 = RowSetProvider.newFactory(DEFFAULT_FACTORY_CLASSNAME, null); - return new Object[][]{ - {rsf, NO_VALADATE_IMPL}, - {rsf, DEFFAULT_FACTORY_CLASSNAME}, - {rsf1, STUB_FACTORY_CLASSNAME}, - {rsf2, DEFFAULT_FACTORY_CLASSNAME} - }; + return Stream.of( + Arguments.of(rsf, NO_VALADATE_IMPL), + Arguments.of(rsf, DEFFAULT_FACTORY_CLASSNAME), + Arguments.of(rsf1, STUB_FACTORY_CLASSNAME), + Arguments.of(rsf2, DEFFAULT_FACTORY_CLASSNAME) + ); } } diff --git a/test/jdk/javax/sql/testng/test/rowset/RowSetWarningTests.java b/test/jdk/javax/sql/test/rowset/RowSetWarningTests.java similarity index 97% rename from test/jdk/javax/sql/testng/test/rowset/RowSetWarningTests.java rename to test/jdk/javax/sql/test/rowset/RowSetWarningTests.java index 41b52c4ef57..81ae97af785 100644 --- a/test/jdk/javax/sql/testng/test/rowset/RowSetWarningTests.java +++ b/test/jdk/javax/sql/test/rowset/RowSetWarningTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -24,8 +24,10 @@ package test.rowset; import java.sql.SQLException; import javax.sql.rowset.RowSetWarning; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class RowSetWarningTests extends BaseTest { diff --git a/test/jdk/javax/sql/testng/test/rowset/cachedrowset/CachedRowSetTests.java b/test/jdk/javax/sql/test/rowset/cachedrowset/CachedRowSetTests.java similarity index 94% rename from test/jdk/javax/sql/testng/test/rowset/cachedrowset/CachedRowSetTests.java rename to test/jdk/javax/sql/test/rowset/cachedrowset/CachedRowSetTests.java index 1155a889187..5fd473924c3 100644 --- a/test/jdk/javax/sql/testng/test/rowset/cachedrowset/CachedRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/cachedrowset/CachedRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 diff --git a/test/jdk/javax/sql/testng/test/rowset/cachedrowset/CommonCachedRowSetTests.java b/test/jdk/javax/sql/test/rowset/cachedrowset/CommonCachedRowSetTests.java similarity index 79% rename from test/jdk/javax/sql/testng/test/rowset/cachedrowset/CommonCachedRowSetTests.java rename to test/jdk/javax/sql/test/rowset/cachedrowset/CommonCachedRowSetTests.java index 642c03c9a45..bc63ce28497 100644 --- a/test/jdk/javax/sql/testng/test/rowset/cachedrowset/CommonCachedRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/cachedrowset/CommonCachedRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -37,6 +37,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.Collection; +import java.util.stream.Stream; import javax.sql.RowSet; import javax.sql.rowset.CachedRowSet; import javax.sql.rowset.RowSetMetaDataImpl; @@ -44,12 +45,18 @@ import javax.sql.rowset.serial.SerialRef; import javax.sql.rowset.spi.SyncFactory; import javax.sql.rowset.spi.SyncProvider; import javax.sql.rowset.spi.SyncProviderException; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import test.rowset.CommonRowSetTests; import util.StubArray; import util.StubRef; @@ -83,52 +90,43 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * DataProvider that uses a RowSet with the COFFEE_HOUSES Table */ - @DataProvider(name = "rowsetUsingCoffeeHouses") - protected Object[][] rowsetUsingCoffeeHouses() throws Exception { - RowSet rs = createCoffeeHousesRowSet(); - return new Object[][]{ - {rs} - }; + protected Stream rowsetUsingCoffeeHouses() throws Exception { + return Stream.of(createCoffeeHousesRowSet()); } /* * DataProvider that uses a RowSet with the COFFEES Table */ - @DataProvider(name = "rowsetUsingCoffees") - protected Object[][] rowsetUsingCoffees() throws Exception { - RowSet rs = createCoffeesRowSet(); - return new Object[][]{ - {rs} - }; + protected Stream rowsetUsingCoffees() throws Exception { + return Stream.of(createCoffeesRowSet()); } /* * DataProvider that uses a RowSet with the DATAYPES Table and * used to validate the various supported data types */ - @DataProvider(name = "rowsetUsingDataTypes") - protected Object[][] rowsetUsingDataTypes() throws Exception { + protected Stream rowsetUsingDataTypes() throws Exception { CachedRowSet rs = createDataTypesRowSet(); - return new Object[][]{ - {rs, JDBCType.INTEGER}, - {rs, JDBCType.CHAR}, - {rs, JDBCType.VARCHAR}, - {rs, JDBCType.BIGINT}, - {rs, JDBCType.BOOLEAN}, - {rs, JDBCType.SMALLINT}, - {rs, JDBCType.DOUBLE}, - {rs, JDBCType.DECIMAL}, - {rs, JDBCType.REAL}, - {rs, JDBCType.TINYINT}, - {rs, JDBCType.DATE}, - {rs, JDBCType.TIME}, - {rs, JDBCType.TIMESTAMP}, - {rs, JDBCType.VARBINARY}, - {rs, JDBCType.ARRAY}, - {rs, JDBCType.REF}, - {rs, JDBCType.FLOAT} - }; + return Stream.of( + Arguments.of(rs, JDBCType.INTEGER), + Arguments.of(rs, JDBCType.CHAR), + Arguments.of(rs, JDBCType.VARCHAR), + Arguments.of(rs, JDBCType.BIGINT), + Arguments.of(rs, JDBCType.BOOLEAN), + Arguments.of(rs, JDBCType.SMALLINT), + Arguments.of(rs, JDBCType.DOUBLE), + Arguments.of(rs, JDBCType.DECIMAL), + Arguments.of(rs, JDBCType.REAL), + Arguments.of(rs, JDBCType.TINYINT), + Arguments.of(rs, JDBCType.DATE), + Arguments.of(rs, JDBCType.TIME), + Arguments.of(rs, JDBCType.TIMESTAMP), + Arguments.of(rs, JDBCType.VARBINARY), + Arguments.of(rs, JDBCType.ARRAY), + Arguments.of(rs, JDBCType.REF), + Arguments.of(rs, JDBCType.FLOAT) + ); } /* @@ -254,7 +252,7 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { private void compareMetaData(ResultSetMetaData rsmd, ResultSetMetaData rsmd1) throws SQLException { - assertEquals(rsmd1.getColumnCount(), rsmd.getColumnCount()); + assertEquals(rsmd.getColumnCount(), rsmd1.getColumnCount()); int cols = rsmd.getColumnCount(); for (int i = 1; i <= cols; i++) { assertTrue(rsmd1.getCatalogName(i).equals(rsmd.getCatalogName(i))); @@ -356,26 +354,33 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate SyncProviderException is thrown when acceptChanges is called * but there is not a way to make a connection to the datasource */ - @Test(dataProvider = "rowSetType", expectedExceptions = SyncProviderException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0000(CachedRowSet rs) throws Exception { - rs.acceptChanges(); - rs.close(); + assertThrows(SyncProviderException.class, () -> { + rs.acceptChanges(); + rs.close(); + }); } /* * Validate SyncProviderException is thrown when acceptChanges is called * when null is passed as the datasource */ - @Test(dataProvider = "rowSetType", expectedExceptions = SyncProviderException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0001(CachedRowSet rs) throws Exception { - rs.acceptChanges(null); - rs.close(); + assertThrows(SyncProviderException.class, () -> { + rs.acceptChanges(null); + rs.close(); + }); } /* * Validate that that RIOPtimsticProvider is the default SyncProvider */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0002(CachedRowSet rs) throws SQLException { SyncProvider sp = rs.getSyncProvider(); assertTrue(sp instanceof com.sun.rowset.providers.RIOptimisticProvider); @@ -385,7 +390,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Validate that you can specify a SyncProvider */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0003(CachedRowSet rs) throws SQLException { // Register a provider and make sure it is avaiable @@ -400,7 +406,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Create a RowSetListener and validate that notifyRowSetChanged is called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0004(CachedRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -412,7 +419,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Create a RowSetListener and validate that notifyRowSetChanged is called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0005(CachedRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -424,7 +432,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Create a RowSetListener and validate that notifyRowChanged is called */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0006(RowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -443,7 +452,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Create a multiple RowSetListeners and validate that notifyRowChanged, * notifiyMoved is called on all listners */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0007(RowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); TestRowSetListener rsl2 = new TestRowSetListener(); @@ -467,7 +477,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Create a RowSetListener and validate that notifyRowChanged and * notifyCursorMoved are called */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0008(CachedRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -487,7 +498,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Create a RowSetListener and validate that notifyCursorMoved is called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0009(RowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -499,7 +511,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Validate that getTableName() returns the proper values */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0010(CachedRowSet rs) throws Exception { assertNull(rs.getTableName()); rs.setTableName(COFFEE_HOUSES_TABLE); @@ -510,12 +523,13 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Validate that getKeyColumns() returns the proper values */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0011(CachedRowSet rs) throws Exception { int[] pkeys = {1, 3}; assertNull(rs.getKeyColumns()); rs.setKeyColumns(pkeys); - assertEquals(rs.getKeyColumns(), pkeys); + assertArrayEquals(pkeys, rs.getKeyColumns()); rs.close(); } @@ -523,52 +537,62 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that setMatchColumn throws a SQLException if the column * index specified is out of range */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0012(CachedRowSet rs) throws Exception { - rs.setMatchColumn(-1); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setMatchColumn(-1); + rs.close(); + }); } /* * Validate that setMatchColumn throws a SQLException if the column * index specified is out of range */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0013(CachedRowSet rs) throws Exception { - int[] cols = {1, -1}; - rs.setMatchColumn(cols); - rs.close(); + assertThrows(SQLException.class, () -> { + int[] cols = {1, -1}; + rs.setMatchColumn(cols); + rs.close(); + }); } /* * Validate that setMatchColumn throws a SQLException if the column * index specified is out of range */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0014(CachedRowSet rs) throws Exception { - rs.setMatchColumn((String) null); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setMatchColumn((String) null); + rs.close(); + }); } /* * Validate that setMatchColumn throws a SQLException if the column * index specified is out of range */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0015(CachedRowSet rs) throws Exception { - String[] cols = {"ID", null}; - rs.setMatchColumn(cols); + assertThrows(SQLException.class, () -> { + String[] cols = {"ID", null}; + rs.setMatchColumn(cols); + }); } /* * Validate that getMatchColumn returns the same value specified by * setMatchColumn */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0016(CachedRowSet rs) throws Exception { int[] expectedCols = {1}; String[] expectedColNames = {"ID"}; @@ -578,8 +602,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { for (int i = 0; i < actualCols.length; i++) { System.out.println(actualCols[i]); } - assertEquals(actualCols, expectedCols); - assertEquals(actualColNames, expectedColNames); + assertArrayEquals(expectedCols, actualCols); + assertArrayEquals(expectedColNames, actualColNames); rs.close(); } @@ -587,15 +611,17 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that getMatchColumn returns the same value specified by * setMatchColumn */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0017(CachedRowSet rs) throws Exception { int[] expectedCols = {1}; String[] expectedColNames = {"ID"}; rs.setMatchColumn(expectedColNames[0]); int[] actualCols = rs.getMatchColumnIndexes(); String[] actualColNames = rs.getMatchColumnNames(); - assertEquals(actualCols, expectedCols); - assertEquals(actualColNames, expectedColNames); + assertArrayEquals(expectedCols, actualCols); + assertArrayEquals(expectedColNames, actualColNames); rs.close(); } @@ -603,16 +629,18 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that getMatchColumn returns the same valid value specified by * setMatchColumn */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0018(CachedRowSet rs) throws Exception { int[] expectedCols = {1, 3}; String[] expectedColNames = {"COF_ID", "SUP_ID"}; rs.setMatchColumn(expectedCols); int[] actualCols = rs.getMatchColumnIndexes(); String[] actualColNames = rs.getMatchColumnNames(); - assertEquals(actualCols, expectedCols); - assertEquals(actualColNames, expectedColNames); - assertEquals(actualCols, expectedCols); + assertArrayEquals(expectedCols, actualCols); + assertArrayEquals(expectedColNames, actualColNames); + assertArrayEquals(expectedCols, actualCols); rs.close(); } @@ -620,15 +648,17 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that getMatchColumn returns the same valid value specified by * setMatchColumn */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0019(CachedRowSet rs) throws Exception { int[] expectedCols = {1, 3}; String[] expectedColNames = {"COF_ID", "SUP_ID"}; rs.setMatchColumn(expectedColNames); int[] actualCols = rs.getMatchColumnIndexes(); String[] actualColNames = rs.getMatchColumnNames(); - assertEquals(actualCols, expectedCols); - assertEquals(actualColNames, expectedColNames); + assertArrayEquals(expectedCols, actualCols); + assertArrayEquals(expectedColNames, actualColNames); rs.close(); } @@ -636,69 +666,78 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that getMatchColumnIndexes throws a SQLException if * unsetMatchColumn has been called */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0020(CachedRowSet rs) throws Exception { - rs.setMatchColumn(1); - int[] actualCols = rs.getMatchColumnIndexes(); - assertTrue(actualCols != null); - rs.unsetMatchColumn(1); - actualCols = rs.getMatchColumnIndexes(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setMatchColumn(1); + int[] actualCols = rs.getMatchColumnIndexes(); + assertTrue(actualCols != null); + rs.unsetMatchColumn(1); + actualCols = rs.getMatchColumnIndexes(); + rs.close(); + }); } /* * Validate that getMatchColumnNames throws a SQLException if * unsetMatchColumn has been called */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0021(CachedRowSet rs) throws Exception { - String matchColumn = "ID"; - rs.setMatchColumn(matchColumn); - String[] actualColNames = rs.getMatchColumnNames(); - assertTrue(actualColNames != null); - rs.unsetMatchColumn(matchColumn); - actualColNames = rs.getMatchColumnNames(); - rs.close(); + assertThrows(SQLException.class, () -> { + String matchColumn = "ID"; + rs.setMatchColumn(matchColumn); + String[] actualColNames = rs.getMatchColumnNames(); + assertTrue(actualColNames != null); + rs.unsetMatchColumn(matchColumn); + actualColNames = rs.getMatchColumnNames(); + rs.close(); + }); } /* * Validate that getMatchColumnIndexes throws a SQLException if * unsetMatchColumn has been called */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0022(CachedRowSet rs) throws Exception { - int[] expectedCols = {1, 3}; - rs.setMatchColumn(expectedCols); - int[] actualCols = rs.getMatchColumnIndexes(); - assertTrue(actualCols != null); - rs.unsetMatchColumn(expectedCols); - actualCols = rs.getMatchColumnIndexes(); - rs.close(); + assertThrows(SQLException.class, () -> { + int[] expectedCols = {1, 3}; + rs.setMatchColumn(expectedCols); + int[] actualCols = rs.getMatchColumnIndexes(); + assertTrue(actualCols != null); + rs.unsetMatchColumn(expectedCols); + actualCols = rs.getMatchColumnIndexes(); + rs.close(); + }); } /* * Validate that getMatchColumnNames throws a SQLException if * unsetMatchColumn has been called */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0023(CachedRowSet rs) throws Exception { - String[] expectedColNames = {"COF_ID", "SUP_ID"}; - rs.setMatchColumn(expectedColNames); - String[] actualColNames = rs.getMatchColumnNames(); - assertTrue(actualColNames != null); - rs.unsetMatchColumn(expectedColNames); - actualColNames = rs.getMatchColumnNames(); - rs.close(); + assertThrows(SQLException.class, () -> { + String[] expectedColNames = {"COF_ID", "SUP_ID"}; + rs.setMatchColumn(expectedColNames); + String[] actualColNames = rs.getMatchColumnNames(); + assertTrue(actualColNames != null); + rs.unsetMatchColumn(expectedColNames); + actualColNames = rs.getMatchColumnNames(); + rs.close(); + }); } /* * Validate size() returns the correct number of rows */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0024(CachedRowSet rs) throws Exception { assertTrue(rs.size() == COFFEE_HOUSES_ROWS); rs.close(); @@ -708,9 +747,10 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that the correct rows are returned comparing the primary * keys */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0025(RowSet rs) throws SQLException { - assertEquals(getPrimaryKeys(rs), COFFEE_HOUSES_PRIMARY_KEYS); + assertArrayEquals(COFFEE_HOUSES_PRIMARY_KEYS, getPrimaryKeys(rs)); rs.close(); } @@ -719,7 +759,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate the visibility of the row depending on the value of * setShowdelete */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0026(CachedRowSet rs) throws Exception { Object[] afterDelete = { 10023, 33002, 10040, 32001, 10042, 10024, 10039, 10041, @@ -727,13 +768,13 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { }; int rowToDelete = 10035; // All rows should be found - assertEquals(getPrimaryKeys(rs), COFFEE_HOUSES_PRIMARY_KEYS); + assertArrayEquals(COFFEE_HOUSES_PRIMARY_KEYS, getPrimaryKeys(rs)); // Delete the row assertTrue(deleteRowByPrimaryKey(rs, rowToDelete, 1)); // With setShowDeleted(false) which is the default, // the deleted row should not be visible assertFalse(findRowByPrimaryKey(rs, rowToDelete, 1)); - assertEquals(getPrimaryKeys(rs), afterDelete); + assertArrayEquals(afterDelete, getPrimaryKeys(rs)); assertTrue(rs.size() == COFFEE_HOUSES_ROWS); // With setShowDeleted(true), the deleted row should be visible rs.setShowDeleted(true); @@ -744,7 +785,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Validate that there is no page size by default */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0027(CachedRowSet rs) throws Exception { assertTrue(rs.getPageSize() == 0); rs.close(); @@ -754,7 +796,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate the value you set via setPageSize is returned by getPageSize * then reset to having no limit */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0028(CachedRowSet rs) throws Exception { int rows = 100; rs.setPageSize(rows); @@ -768,30 +811,39 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate SQLException is thrown when an invalid value is specified * for setPageSize */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0029(CachedRowSet rs) throws Exception { - rs.setPageSize(-1); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setPageSize(-1); + rs.close(); + }); } /* * Validate SQLException is thrown when nextPage is called without a * call to populate or execute */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0030(CachedRowSet rs) throws Exception { - rs.nextPage(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.nextPage(); + rs.close(); + }); } /* * Validate SQLException is thrown when previousPage is called without a * call to populate or execute */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0031(CachedRowSet rs) throws Exception { - rs.previousPage(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.previousPage(); + rs.close(); + }); } @@ -799,40 +851,48 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate SQLException is thrown when execute is called * but there is not a way to make a connection to the datasource */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0032(CachedRowSet rs) throws Exception { - rs.execute(null); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.execute(null); + rs.close(); + }); } /* * Validate SQLException is thrown when execute is called * but there is not a way to make a connection to the datasource */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0033(CachedRowSet rs) throws Exception { - rs.execute(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.execute(); + rs.close(); + }); } /* * Validate that toCollection() returns the proper values */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0034(CachedRowSet rs) throws Exception { Object[] cities = {"Mendocino", "Seattle", "SF", "Portland", "SF", "Sacramento", "Carmel", "LA", "Olympia", "Seattle", "SF", "LA", "San Jose", "Eugene"}; rs.beforeFirst(); - assertEquals(rs.toCollection(2).toArray(), cities); - assertEquals(rs.toCollection("CITY").toArray(), cities); + assertArrayEquals(cities, rs.toCollection(2).toArray()); + assertArrayEquals(cities, rs.toCollection("CITY").toArray()); rs.close(); } /* * Validate that toCollection() returns the proper values */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0035(CachedRowSet rs) throws Exception { Collection col = rs.toCollection(); assertTrue(rs.size() == col.size()); @@ -850,7 +910,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Validate that createCopy() returns the proper values */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0036(CachedRowSet rs) throws Exception { try (CachedRowSet crs1 = rs.createCopy()) { compareRowSets(rs, crs1); @@ -861,7 +922,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Validate that createCopySchema() returns the proper values */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0037(CachedRowSet rs) throws Exception { try (CachedRowSet crs1 = rs.createCopySchema()) { assertTrue(crs1.size() == 0); @@ -875,7 +937,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * and getMatchColumnIndexes should throw a SQLException. This test * specifies setMatchColumn(int) */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0038(CachedRowSet rs) throws Exception { rs.setMatchColumn(1); try (CachedRowSet crs1 = rs.createCopyNoConstraints()) { @@ -904,7 +967,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * and getMatchColumnIndexes should throw a SQLException. This test * specifies setMatchColumn(String) */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0039(CachedRowSet rs) throws Exception { rs.setMatchColumn("ID"); try (CachedRowSet crs1 = rs.createCopyNoConstraints()) { @@ -932,7 +996,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that columnUpdated works with the various datatypes specifying * the column index */ - @Test(dataProvider = "rowsetUsingDataTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingDataTypes") public void commonCachedRowSetTest0040(CachedRowSet rs, JDBCType type) throws Exception { rs.beforeFirst(); assertTrue(rs.next()); @@ -1029,7 +1094,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that columnUpdated works with the various datatypes specifying * the column name */ - @Test(dataProvider = "rowsetUsingDataTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingDataTypes") public void commonCachedRowSetTest0041(CachedRowSet rs, JDBCType type) throws Exception { rs.beforeFirst(); assertTrue(rs.next()); @@ -1127,7 +1193,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate isBeforeFirst(), isFirst() and first() return the correct * results */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0042(RowSet rs) throws Exception { assertFalse(rs.isBeforeFirst()); assertFalse(rs.isFirst()); @@ -1150,7 +1217,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate isAfterLast(), isLast() and last() return the correct * results */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0043(RowSet rs) throws Exception { assertFalse(rs.isAfterLast()); assertFalse(rs.isLast()); @@ -1173,121 +1241,140 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate a SQLException is thrown when undoDelete is called on the * insertRow */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0044(CachedRowSet rs) throws Exception { - rs.insertRow(); - rs.undoDelete(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.insertRow(); + rs.undoDelete(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoDelete is called when * cursor is before the first row */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0045(CachedRowSet rs) throws Exception { - rs.setShowDeleted(true); - rs.beforeFirst(); - rs.undoDelete(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setShowDeleted(true); + rs.beforeFirst(); + rs.undoDelete(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoDelete is called when * cursor is after the last row */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0046(CachedRowSet rs) throws Exception { - rs.setShowDeleted(true); - rs.afterLast(); - rs.undoDelete(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setShowDeleted(true); + rs.afterLast(); + rs.undoDelete(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoUpdate is called on the * insertRow */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0047(CachedRowSet rs) throws Exception { - rs.insertRow(); - rs.undoUpdate(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.insertRow(); + rs.undoUpdate(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoUpdate is called when * cursor is before the first row */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0048(CachedRowSet rs) throws Exception { - rs.setShowDeleted(true); - rs.beforeFirst(); - rs.undoUpdate(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setShowDeleted(true); + rs.beforeFirst(); + rs.undoUpdate(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoUpdate is called when * cursor is after the last row */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0049(CachedRowSet rs) throws Exception { - rs.setShowDeleted(true); - rs.afterLast(); - rs.undoUpdate(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setShowDeleted(true); + rs.afterLast(); + rs.undoUpdate(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoInsert is called on the * insertRow */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0050(CachedRowSet rs) throws Exception { - rs.insertRow(); - rs.undoInsert(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.insertRow(); + rs.undoInsert(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoInsert is called when * cursor is before the first row */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0051(CachedRowSet rs) throws Exception { - rs.setShowDeleted(true); - rs.beforeFirst(); - rs.undoInsert(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setShowDeleted(true); + rs.beforeFirst(); + rs.undoInsert(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoInsert is called when * cursor is after the last row */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0052(CachedRowSet rs) throws Exception { - rs.setShowDeleted(true); - rs.afterLast(); - rs.undoInsert(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setShowDeleted(true); + rs.afterLast(); + rs.undoInsert(); + rs.close(); + }); } /* * Insert a row, then call undoInsert to roll back the insert and validate * the row is not there */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0053(CachedRowSet rs) throws Exception { int rowToInsert = 1961; assertTrue(rs.size() == COFFEE_HOUSES_ROWS); @@ -1314,7 +1401,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Insert a row, delete the row and then call undoDelete to make sure it * is comes back */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0054(CachedRowSet rs) throws Exception { int rowToDelete = 1961; assertTrue(rs.size() == COFFEE_HOUSES_ROWS); @@ -1348,7 +1436,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Insert a row, modify a field and then call undoUpdate to revert the * insert */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0055(CachedRowSet rs) throws Exception { int rowToInsert = 1961; assertTrue(rs.size() == COFFEE_HOUSES_ROWS); @@ -1385,7 +1474,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate getOriginal returns a ResultSet which is a copy of the original * RowSet */ - @Test(dataProvider = "rowsetUsingCoffees") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void commonCachedRowSetTest0056(CachedRowSet rs) throws Exception { String coffee = "Hazelnut"; int sales = 100; @@ -1414,12 +1504,12 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { assertTrue(rs1.getInt(1) == origId); assertTrue(rs1.getString(2).equals(origCoffee)); assertTrue(rs1.getInt(5) == origSales); - assertEquals(getPrimaryKeys(rs1), COFFEES_PRIMARY_KEYS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(rs1)); // Check current rowset assertTrue(rs.getInt(1) == id); assertTrue(rs.getString(2).equals(coffee)); assertTrue(rs.getInt(5) == sales); - assertEquals(getPrimaryKeys(rs), updatedPkeys); + assertArrayEquals(updatedPkeys, getPrimaryKeys(rs)); } rs.close(); } @@ -1428,7 +1518,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate getOriginalRow returns a ResultSet which is a copy of the * original row that was modified */ - @Test(dataProvider = "rowsetUsingCoffees") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void commonCachedRowSetTest0057(CachedRowSet rs) throws Exception { String coffee = "Hazelnut"; int sales = 100; @@ -1463,7 +1554,7 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { assertTrue(rs.getInt(1) == id); assertTrue(rs.getString(2).equals(coffee)); assertTrue(rs.getInt(5) == sales); - assertEquals(getPrimaryKeys(rs), updatedPkeys); + assertArrayEquals(updatedPkeys, getPrimaryKeys(rs)); } rs.close(); } @@ -1472,7 +1563,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that restoreOrginal will restore the RowSet to its * state prior to the insert of a row */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0058(CachedRowSet rs) throws Exception { int rowToInsert = 1961; assertTrue(rs.size() == COFFEE_HOUSES_ROWS); @@ -1509,7 +1601,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that restoreOrginal will restore the RowSet to its * state prior to deleting a row */ - @Test(dataProvider = "rowsetUsingCoffees", enabled = true) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void commonCachedRowSetTest0059(CachedRowSet rs) throws Exception { int rowToDelete = 2; try (CachedRowSet crs1 = rsf.createCachedRowSet()) { @@ -1540,7 +1633,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that restoreOrginal will restore the RowSet to its * state prior to updating a row */ - @Test(dataProvider = "rowsetUsingCoffees", enabled = true) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void commonCachedRowSetTest0060(CachedRowSet rs) throws Exception { int rowToUpdate = 2; String coffee = "Hazelnut"; @@ -1578,7 +1672,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Initialize a RowSet via the populate method. Validate it matches * the original ResultSet */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0061(CachedRowSet rs) throws Exception { try (CachedRowSet crs1 = rsf.createCachedRowSet()) { rs.beforeFirst(); @@ -1593,7 +1688,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate it matches the original ResultSet starting for the specofied * offset */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0062(CachedRowSet rs) throws Exception { Object[] expectedRows = { 32001, 10042, 10024, 10039, 10041, 33005, 33010, 10035, 10037, @@ -1603,8 +1699,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { try (CachedRowSet crs1 = rsf.createCachedRowSet()) { rs.beforeFirst(); crs1.populate(rs, startingRow); - assertEquals(crs1.size(), COFFEE_HOUSES_ROWS - startingRow + 1); - assertEquals(getPrimaryKeys(crs1), expectedRows); + assertEquals(COFFEE_HOUSES_ROWS - startingRow + 1, crs1.size()); + assertArrayEquals(expectedRows, getPrimaryKeys(crs1)); } rs.close(); } diff --git a/test/jdk/javax/sql/testng/test/rowset/filteredrowset/CityFilter.java b/test/jdk/javax/sql/test/rowset/filteredrowset/CityFilter.java similarity index 97% rename from test/jdk/javax/sql/testng/test/rowset/filteredrowset/CityFilter.java rename to test/jdk/javax/sql/test/rowset/filteredrowset/CityFilter.java index 2188c4eed08..458088ccb45 100644 --- a/test/jdk/javax/sql/testng/test/rowset/filteredrowset/CityFilter.java +++ b/test/jdk/javax/sql/test/rowset/filteredrowset/CityFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 diff --git a/test/jdk/javax/sql/testng/test/rowset/filteredrowset/FilteredRowSetTests.java b/test/jdk/javax/sql/test/rowset/filteredrowset/FilteredRowSetTests.java similarity index 81% rename from test/jdk/javax/sql/testng/test/rowset/filteredrowset/FilteredRowSetTests.java rename to test/jdk/javax/sql/test/rowset/filteredrowset/FilteredRowSetTests.java index 263a79a68b6..55d9323f373 100644 --- a/test/jdk/javax/sql/testng/test/rowset/filteredrowset/FilteredRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/filteredrowset/FilteredRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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,24 +26,30 @@ import java.sql.SQLException; import javax.sql.RowSet; import javax.sql.rowset.FilteredRowSet; import javax.sql.rowset.Predicate; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.AfterEach; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + import test.rowset.webrowset.CommonWebRowSetTests; public class FilteredRowSetTests extends CommonWebRowSetTests { private FilteredRowSet frs; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { frs = createCoffeeHousesRowSet(); } - @AfterMethod + @AfterEach public void tearDownMethod() throws Exception { frs.close(); } @@ -83,7 +89,7 @@ public class FilteredRowSetTests extends CommonWebRowSetTests { 10023, 10040, 10042, 10024, 10039, 10041, 10035, 10037, 10034 }; frs.setFilter(new PrimaryKeyFilter(10000, 10999, 1)); - assertEquals(getPrimaryKeys(frs), expectedKeys); + assertArrayEquals(expectedKeys, getPrimaryKeys(frs)); } /* @@ -96,7 +102,7 @@ public class FilteredRowSetTests extends CommonWebRowSetTests { 10023, 10040, 10042, 10024, 10039, 10041, 10035, 10037, 10034 }; frs.setFilter(new PrimaryKeyFilter(10000, 10999, "STORE_ID")); - assertEquals(getPrimaryKeys(frs), expectedKeys); + assertArrayEquals(expectedKeys, getPrimaryKeys(frs)); } @@ -111,7 +117,7 @@ public class FilteredRowSetTests extends CommonWebRowSetTests { }; String[] cityArray = {"SF", "LA"}; frs.setFilter(new CityFilter(cityArray, 2)); - assertEquals(getPrimaryKeys(frs), expectedKeys); + assertArrayEquals(expectedKeys, getPrimaryKeys(frs)); } /* @@ -125,14 +131,16 @@ public class FilteredRowSetTests extends CommonWebRowSetTests { }; String[] cityArray = {"SF", "LA"}; frs.setFilter(new CityFilter(cityArray, "CITY")); - assertEquals(getPrimaryKeys(frs), expectedKeys); + assertArrayEquals(expectedKeys, getPrimaryKeys(frs)); } // Tests that are common but need to be disabled due to an implementation bug - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0043(RowSet rs) throws Exception { // Need to fix bug in FilteredRowSets } diff --git a/test/jdk/javax/sql/testng/test/rowset/filteredrowset/PrimaryKeyFilter.java b/test/jdk/javax/sql/test/rowset/filteredrowset/PrimaryKeyFilter.java similarity index 97% rename from test/jdk/javax/sql/testng/test/rowset/filteredrowset/PrimaryKeyFilter.java rename to test/jdk/javax/sql/test/rowset/filteredrowset/PrimaryKeyFilter.java index 2a748314006..0353312419c 100644 --- a/test/jdk/javax/sql/testng/test/rowset/filteredrowset/PrimaryKeyFilter.java +++ b/test/jdk/javax/sql/test/rowset/filteredrowset/PrimaryKeyFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 diff --git a/test/jdk/javax/sql/testng/test/rowset/jdbcrowset/JdbcRowSetDriverManagerTest.java b/test/jdk/javax/sql/test/rowset/jdbcrowset/JdbcRowSetDriverManagerTest.java similarity index 95% rename from test/jdk/javax/sql/testng/test/rowset/jdbcrowset/JdbcRowSetDriverManagerTest.java rename to test/jdk/javax/sql/test/rowset/jdbcrowset/JdbcRowSetDriverManagerTest.java index be86798177a..5b135e16b5c 100644 --- a/test/jdk/javax/sql/testng/test/rowset/jdbcrowset/JdbcRowSetDriverManagerTest.java +++ b/test/jdk/javax/sql/test/rowset/jdbcrowset/JdbcRowSetDriverManagerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -28,7 +28,9 @@ import java.sql.SQLException; import javax.sql.rowset.JdbcRowSet; import javax.sql.rowset.RowSetFactory; import javax.sql.rowset.RowSetProvider; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubDriver; @@ -41,7 +43,7 @@ public class JdbcRowSetDriverManagerTest extends BaseTest { * Validate that JDBCRowSetImpl can connect to a JDBC driver that is * register by DriverManager. */ - @Test(enabled = true) + @Test public void test0000() throws SQLException { DriverManager.registerDriver(new StubDriver()); diff --git a/test/jdk/javax/sql/testng/test/rowset/joinrowset/JoinRowSetTests.java b/test/jdk/javax/sql/test/rowset/joinrowset/JoinRowSetTests.java similarity index 83% rename from test/jdk/javax/sql/testng/test/rowset/joinrowset/JoinRowSetTests.java rename to test/jdk/javax/sql/test/rowset/joinrowset/JoinRowSetTests.java index b84453e9a3b..92ce09bcf85 100644 --- a/test/jdk/javax/sql/testng/test/rowset/joinrowset/JoinRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/joinrowset/JoinRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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,16 +26,22 @@ import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; import javax.sql.RowSet; import javax.sql.rowset.CachedRowSet; import javax.sql.rowset.JoinRowSet; import javax.sql.rowset.RowSetMetaDataImpl; import javax.sql.rowset.WebRowSet; -import static org.testng.Assert.assertEquals; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import test.rowset.webrowset.CommonWebRowSetTests; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + public class JoinRowSetTests extends CommonWebRowSetTests { private final String SUPPLIERS_TABLE = "SUPPLIERS"; @@ -150,8 +156,7 @@ public class JoinRowSetTests extends CommonWebRowSetTests { /* * DataProvider used to set parameters for basic types that are supported */ - @DataProvider(name = "createCachedRowSetsToUse") - private Object[][] createCachedRowSetsToUse() throws SQLException { + private Stream createCachedRowSetsToUse() throws SQLException { CachedRowSet crs = rsf.createCachedRowSet(); initCoffeesMetaData(crs); createCoffeesRows(crs); @@ -162,9 +167,7 @@ public class JoinRowSetTests extends CommonWebRowSetTests { createSuppiersRows(crs1); // Make sure you are not on the insertRow crs1.moveToCurrentRow(); - return new Object[][]{ - {crs, crs1} - }; + return Stream.of(Arguments.of(crs, crs1)); } /* @@ -178,13 +181,14 @@ public class JoinRowSetTests extends CommonWebRowSetTests { results.add(jrs.getInt("COF_ID")); } } - assertEquals(results.toArray(), EXPECTED); + assertArrayEquals(EXPECTED, results.toArray()); } /* * Join two CachedRowSets specifying a column name to join against */ - @Test(dataProvider = "createCachedRowSetsToUse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("createCachedRowSetsToUse") public void joinRowSetTests0000(CachedRowSet crs, CachedRowSet crs1) throws Exception { @@ -200,7 +204,8 @@ public class JoinRowSetTests extends CommonWebRowSetTests { /* * Join two CachedRowSets specifying a column index to join against */ - @Test(dataProvider = "createCachedRowSetsToUse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("createCachedRowSetsToUse") public void joinRowSetTests0001(CachedRowSet crs, CachedRowSet crs1) throws Exception { @@ -216,7 +221,8 @@ public class JoinRowSetTests extends CommonWebRowSetTests { /* * Join two CachedRowSets specifying a column name to join against */ - @Test(dataProvider = "createCachedRowSetsToUse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("createCachedRowSetsToUse") public void joinRowSetTests0002(CachedRowSet crs, CachedRowSet crs1) throws Exception { @@ -233,7 +239,8 @@ public class JoinRowSetTests extends CommonWebRowSetTests { /* * Join two CachedRowSets specifying a column index to join against */ - @Test(dataProvider = "createCachedRowSetsToUse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("createCachedRowSetsToUse") public void joinRowSetTests0003(CachedRowSet crs, CachedRowSet crs1) throws Exception { @@ -251,7 +258,8 @@ public class JoinRowSetTests extends CommonWebRowSetTests { /* * Join two CachedRowSets specifying a column name to join against */ - @Test(dataProvider = "createCachedRowSetsToUse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("createCachedRowSetsToUse") public void joinRowSetTests0005(CachedRowSet crs, CachedRowSet crs1) throws Exception { @@ -269,7 +277,8 @@ public class JoinRowSetTests extends CommonWebRowSetTests { /* * Join two CachedRowSets specifying a column index to join against */ - @Test(dataProvider = "createCachedRowSetsToUse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("createCachedRowSetsToUse") public void joinRowSetTests0006(CachedRowSet crs, CachedRowSet crs1) throws Exception { @@ -286,39 +295,56 @@ public class JoinRowSetTests extends CommonWebRowSetTests { } // Disabled tests due to bugs in JoinRowSet - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0004(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0005(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0008(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0026(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0027(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0053(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0054(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0055(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0009(WebRowSet wrs1) throws Exception { } } diff --git a/test/jdk/javax/sql/test/rowset/resourcebundle/TEST.properties b/test/jdk/javax/sql/test/rowset/resourcebundle/TEST.properties new file mode 100644 index 00000000000..045227a0c2b --- /dev/null +++ b/test/jdk/javax/sql/test/rowset/resourcebundle/TEST.properties @@ -0,0 +1,2 @@ +# Enabled for ValidateGetBundle.java +modules = java.sql.rowset/com.sun.rowset:+open diff --git a/test/jdk/javax/sql/resourceBundleTests/ValidateGetBundle.java b/test/jdk/javax/sql/test/rowset/resourcebundle/ValidateGetBundle.java similarity index 94% rename from test/jdk/javax/sql/resourceBundleTests/ValidateGetBundle.java rename to test/jdk/javax/sql/test/rowset/resourcebundle/ValidateGetBundle.java index be17caed5c8..63f12740c09 100644 --- a/test/jdk/javax/sql/resourceBundleTests/ValidateGetBundle.java +++ b/test/jdk/javax/sql/test/rowset/resourcebundle/ValidateGetBundle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -20,6 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +package test.rowset.resourcebundle; import com.sun.rowset.JdbcRowSetResourceBundle; @@ -35,8 +36,6 @@ import org.junit.jupiter.params.provider.Arguments; * @test * @bug 8294989 * @summary Check JDBC RowSet resource bundle access - * @modules java.sql.rowset/com.sun.rowset:+open - * @run junit/othervm ValidateGetBundle */ public class ValidateGetBundle{ diff --git a/test/jdk/javax/sql/testng/test/rowset/ValidateResourceBundleAccess.java b/test/jdk/javax/sql/test/rowset/resourcebundle/ValidateResourceBundleAccess.java similarity index 89% rename from test/jdk/javax/sql/testng/test/rowset/ValidateResourceBundleAccess.java rename to test/jdk/javax/sql/test/rowset/resourcebundle/ValidateResourceBundleAccess.java index 7591891c42b..5deca26cb25 100644 --- a/test/jdk/javax/sql/testng/test/rowset/ValidateResourceBundleAccess.java +++ b/test/jdk/javax/sql/test/rowset/resourcebundle/ValidateResourceBundleAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -20,21 +20,21 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.rowset; +package test.rowset.resourcebundle; import java.util.Locale; import java.sql.SQLException; import javax.sql.rowset.RowSetProvider; -import org.testng.annotations.Test; -import org.testng.annotations.BeforeClass; -import static org.testng.Assert.*; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; /** * @test * @bug 8294989 * @summary Check that the resource bundle can be accessed * @throws SQLException if an unexpected error occurs - * @run testng/othervm */ public class ValidateResourceBundleAccess{ // Expected JDBCResourceBundle message, jdbcrowsetimpl.invalstate @@ -43,8 +43,8 @@ public class ValidateResourceBundleAccess{ private static final String RSREADERERROR = "Internal Error in RowSetReader: no connection or command"; // Checking against English messages, set to US Locale - @BeforeClass - public void setEnglishEnvironment() { + @BeforeAll + public static void setEnglishEnvironment() { Locale.setDefault(Locale.US); } diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SQLInputImplTests.java b/test/jdk/javax/sql/test/rowset/serial/SQLInputImplTests.java similarity index 92% rename from test/jdk/javax/sql/testng/test/rowset/serial/SQLInputImplTests.java rename to test/jdk/javax/sql/test/rowset/serial/SQLInputImplTests.java index 5bd10ed8848..33efda61c4e 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SQLInputImplTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SQLInputImplTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -33,9 +33,11 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; import javax.sql.rowset.serial.SQLInputImpl; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubArray; import util.StubBlob; @@ -54,7 +56,7 @@ public class SQLInputImplTests extends BaseTest { private SuperHero hero; private final String sqlType = "SUPERHERO"; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { map = new HashMap<>(); impl = new TestSQLDataImpl("TestSQLData"); @@ -68,18 +70,22 @@ public class SQLInputImplTests extends BaseTest { * Validate that a SQLException is thrown if the attribute value is * null */ - @Test(expectedExceptions = SQLException.class) + @Test public void test() throws Exception { - SQLInputImpl x = new SQLInputImpl(null, map); + assertThrows(SQLException.class, () -> { + SQLInputImpl x = new SQLInputImpl(null, map); + }); } /* * Validate that a SQLException is thrown if the map value is * null */ - @Test(expectedExceptions = SQLException.class) + @Test public void test02() throws Exception { - SQLInputImpl x = new SQLInputImpl(typeValues, null); + assertThrows(SQLException.class, () -> { + SQLInputImpl x = new SQLInputImpl(typeValues, null); + }); } /* @@ -124,7 +130,7 @@ public class SQLInputImplTests extends BaseTest { /* * Validate a Array can be read */ - @Test(enabled = true) + @Test public void test06() throws Exception { Object[] coffees = new Object[]{"Espresso", "Colombian", "French Roast", "Cappuccino"}; @@ -139,7 +145,7 @@ public class SQLInputImplTests extends BaseTest { /* * Validate a Blob can be read */ - @Test(enabled = true) + @Test public void test07() throws Exception { Blob b = new StubBlob(); Object[] values = {b}; @@ -153,7 +159,7 @@ public class SQLInputImplTests extends BaseTest { /* * Validate a Clob can be read */ - @Test(enabled = true) + @Test public void test08() throws Exception { Clob c = new StubClob(); Object[] values = {c}; @@ -166,7 +172,7 @@ public class SQLInputImplTests extends BaseTest { /* * Validate a Ref can be read */ - @Test(enabled = true) + @Test public void test09() throws Exception { Ref ref = new StubRef(sqlType, hero); Object[] values = {ref}; @@ -179,7 +185,7 @@ public class SQLInputImplTests extends BaseTest { /* * Validate a URL can be read */ - @Test(enabled = true) + @Test public void test10() throws Exception { URL u = new URL("http://www.oracle.com/"); Object[] values = {u}; diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SQLOutputImplTests.java b/test/jdk/javax/sql/test/rowset/serial/SQLOutputImplTests.java similarity index 91% rename from test/jdk/javax/sql/testng/test/rowset/serial/SQLOutputImplTests.java rename to test/jdk/javax/sql/test/rowset/serial/SQLOutputImplTests.java index 1f90c9981a7..83712e81396 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SQLOutputImplTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SQLOutputImplTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -41,9 +41,11 @@ import javax.sql.rowset.serial.SerialClob; import javax.sql.rowset.serial.SerialDatalink; import javax.sql.rowset.serial.SerialRef; import javax.sql.rowset.serial.SerialStruct; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubArray; import util.StubBlob; @@ -64,7 +66,7 @@ public class SQLOutputImplTests extends BaseTest { private SuperHero hero; private SQLOutputImpl outImpl; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { results = new Vector(); impl = new TestSQLDataImpl("TestSQLData"); @@ -78,18 +80,22 @@ public class SQLOutputImplTests extends BaseTest { * Validate that a SQLException is thrown if the attribute value is * null */ - @Test(expectedExceptions = SQLException.class) + @Test public void test() throws Exception { - SQLOutputImpl x = new SQLOutputImpl(null, map); + assertThrows(SQLException.class, () -> { + SQLOutputImpl x = new SQLOutputImpl(null, map); + }); } /* * Validate that a SQLException is thrown if the map value is * null */ - @Test(expectedExceptions = SQLException.class) + @Test public void test02() throws Exception { - SQLOutputImpl x = new SQLOutputImpl(results, null); + assertThrows(SQLException.class, () -> { + SQLOutputImpl x = new SQLOutputImpl(results, null); + }); } /* @@ -109,7 +115,7 @@ public class SQLOutputImplTests extends BaseTest { /* * Validate a Array can be written and returned */ - @Test(enabled = true) + @Test public void test04() throws Exception { Object[] coffees = new Object[]{"Espresso", "Colombian", "French Roast", "Cappuccino"}; @@ -123,7 +129,7 @@ public class SQLOutputImplTests extends BaseTest { /* * Validate a Blob can be written and returned */ - @Test(enabled = true) + @Test public void test05() throws Exception { Blob b = new StubBlob(); outImpl.writeBlob(b); @@ -136,7 +142,7 @@ public class SQLOutputImplTests extends BaseTest { /* * Validate a Clob can be written and returned */ - @Test(enabled = true) + @Test public void test06() throws Exception { Clob c = new StubClob(); outImpl.writeClob(c); @@ -148,7 +154,7 @@ public class SQLOutputImplTests extends BaseTest { /* * Validate a Ref can be written and returned */ - @Test(enabled = true) + @Test public void test07() throws Exception { Ref ref = new StubRef(sqlType, hero); outImpl.writeRef(ref); @@ -159,7 +165,7 @@ public class SQLOutputImplTests extends BaseTest { /* * Validate a Struct can be written and returned */ - @Test(enabled = true) + @Test public void test08() throws Exception { Object[] attributes = new Object[]{"Bruce", "Wayne", 1939, "Batman"}; @@ -173,7 +179,7 @@ public class SQLOutputImplTests extends BaseTest { /* * Validate a DataLink can be written and returned */ - @Test(enabled = true) + @Test public void test09() throws Exception { URL u = new URL("http://www.oracle.com/"); outImpl.writeURL(u); @@ -186,7 +192,7 @@ public class SQLOutputImplTests extends BaseTest { /* * Validate an Object implementing SQLData can be written and returned */ - @Test(enabled = true) + @Test public void test10() throws Exception { Object[] attributes = new Object[]{"Bruce", "Wayne", 1939, "Batman"}; diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialArrayTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialArrayTests.java similarity index 72% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialArrayTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialArrayTests.java index 345315b7f05..892de9416a1 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialArrayTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialArrayTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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,9 +29,11 @@ import java.util.HashMap; import java.util.Map; import javax.sql.rowset.serial.SerialArray; import javax.sql.rowset.serial.SerialException; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubArray; @@ -42,7 +44,7 @@ public class SerialArrayTests extends BaseTest { private Array a; private Map> map; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { coffees = new Object[]{"Espresso", "Colombian", "French Roast", "Cappuccino"}; @@ -61,111 +63,133 @@ public class SerialArrayTests extends BaseTest { /* * Validate a SQLException is thrown if the map is null */ - @Test(expectedExceptions = SQLException.class) + @Test public void test02() throws Exception { - SerialArray sa = new SerialArray(a, null); + assertThrows(SQLException.class, () -> { + SerialArray sa = new SerialArray(a, null); + }); } /* * Validate a SerialException is thrown when getResultSet() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test03() throws Exception { - SerialArray sa = new SerialArray(a); - sa.getResultSet(); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.getResultSet(); + }); } /* * Validate a SerialException is thrown when getResultSet() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test04() throws Exception { - SerialArray sa = new SerialArray(a); - sa.getResultSet(null); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.getResultSet(null); + }); } /* * Validate a SerialException is thrown when getResultSet() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test05() throws Exception { - SerialArray sa = new SerialArray(a); - sa.getResultSet(1, 1); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.getResultSet(1, 1); + }); } /* * Validate a SerialException is thrown when getResultSet() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test06() throws Exception { - SerialArray sa = new SerialArray(a); - sa.getResultSet(1, 1, null); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.getResultSet(1, 1, null); + }); } /* * Validate a SerialException is thrown when getArray() is invoked after * free() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test07() throws Exception { - SerialArray sa = new SerialArray(a); - sa.free(); - sa.getArray(); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.free(); + sa.getArray(); + }); } /* * Validate a SerialException is thrown when getArray() is invoked after * free() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test08() throws Exception { - SerialArray sa = new SerialArray(a); - sa.free(); - sa.getArray(map); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.free(); + sa.getArray(map); + }); } /* * Validate a SerialException is thrown when getArray() is invoked after * free() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test09() throws Exception { - SerialArray sa = new SerialArray(a); - sa.free(); - sa.getArray(1, 1, map); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.free(); + sa.getArray(1, 1, map); + }); } /* * Validate a SerialException is thrown when getArray() is invoked after * free() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test10() throws Exception { - SerialArray sa = new SerialArray(a); - sa.free(); - sa.getArray(1, 1); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.free(); + sa.getArray(1, 1); + }); } /* * Validate a SerialException is thrown when getBaseType() is invoked after * free() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test11() throws Exception { - SerialArray sa = new SerialArray(a); - sa.free(); - sa.getBaseType(); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.free(); + sa.getBaseType(); + }); } /* * Validate a SerialException is thrown when getBaseTypeName() is invoked after * free() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test12() throws Exception { - SerialArray sa = new SerialArray(a); - sa.free(); - sa.getBaseTypeName(); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.free(); + sa.getBaseTypeName(); + }); } /* diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialBlobTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialBlobTests.java similarity index 69% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialBlobTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialBlobTests.java index f3f6c327683..f4a32bc19d7 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialBlobTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialBlobTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -24,11 +24,14 @@ package test.rowset.serial; import java.io.InputStream; import java.io.OutputStream; +import java.sql.SQLException; import java.util.Arrays; import javax.sql.rowset.serial.SerialBlob; import javax.sql.rowset.serial.SerialException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubBlob; @@ -37,6 +40,16 @@ public class SerialBlobTests extends BaseTest { // byte[] used to populate SerialBlob private byte[] bytes = new byte[]{1, 2, 3, 4, 5}; + /* + * Validate calling setBinaryStream() on a SerialBlob constructed from a + * byte array throws SerialException. Bug 7077451. + */ + @Test + void setBinaryStreamExceptionTest() throws SQLException { + SerialBlob blob = new SerialBlob(new byte[0]); + assertThrows(SerialException.class, () -> blob.setBinaryStream(0)); + } + /* * Validate calling free() does not throw an Exception */ @@ -50,110 +63,130 @@ public class SerialBlobTests extends BaseTest { * Validate calling getBinaryStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test01() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.getBinaryStream(); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.getBinaryStream(); + }); } /* * Validate calling getBinaryStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test02() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.getBinaryStream(1, 5); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.getBinaryStream(1, 5); + }); } /* * Validate calling getBytes() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test03() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.getBytes(1, 1); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.getBytes(1, 1); + }); } /* * Validate calling getLength() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test04() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.length(); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.length(); + }); } /* * Validate calling position() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test05() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.position(new byte[5], 1); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.position(new byte[5], 1); + }); } /* * Validate calling position() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test06() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.position(new StubBlob(), 1); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.position(new StubBlob(), 1); + }); } /* * Validate calling free() after calling setBinaryStream() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test07() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.setBinaryStream(5); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.setBinaryStream(5); + }); } /* * Validate calling free() after calling setBytes() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test08() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.setBytes(1, new byte[5]); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.setBytes(1, new byte[5]); + }); } /* * Validate calling setBytes() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test09() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.setBytes(1, new byte[10], 0, 5); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.setBytes(1, new byte[10], 0, 5); + }); } /* * Validate calling truncate() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test10() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.truncate(1); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.truncate(1); + }); } /* @@ -173,56 +206,68 @@ public class SerialBlobTests extends BaseTest { /* * Validate a SerialException is thrown if pos < 0 for getBinaryStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test12() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - InputStream is = sb.getBinaryStream(-1, 3); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + InputStream is = sb.getBinaryStream(-1, 3); + }); } /* * Validate a SerialException is thrown if pos = 0 for getBinaryStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test13() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - InputStream is = sb.getBinaryStream(0, 3); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + InputStream is = sb.getBinaryStream(0, 3); + }); } /* * Validate a SerialException is thrown if len > the length of the stream * for getBinaryStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test14() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - InputStream is = sb.getBinaryStream(0, 3); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + InputStream is = sb.getBinaryStream(0, 3); + }); } /* * Validate a SerialException is thrown if length < 1 */ - @Test(expectedExceptions = SerialException.class) + @Test public void test15() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - InputStream is = sb.getBinaryStream(1, 0); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + InputStream is = sb.getBinaryStream(1, 0); + }); } /* * Validate a SerialException is thrown if length > byte array length */ - @Test(expectedExceptions = SerialException.class) + @Test public void test16() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - InputStream is = sb.getBinaryStream(1, 6); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + InputStream is = sb.getBinaryStream(1, 6); + }); } /* * Validate a SerialException is thrown if pos > byte array length */ - @Test(expectedExceptions = SerialException.class) + @Test public void test17() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - InputStream is = sb.getBinaryStream(bytes.length + 2, 6); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + InputStream is = sb.getBinaryStream(bytes.length + 2, 6); + }); } /* @@ -241,12 +286,14 @@ public class SerialBlobTests extends BaseTest { /* * Test clone after free has been called that the clone is not accessible */ - @Test(expectedExceptions = SerialException.class) + @Test public void test19() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - sb.free(); - SerialBlob sb2 = (SerialBlob) sb.clone(); - InputStream is = sb2.getBinaryStream(1, 3); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + sb.free(); + SerialBlob sb2 = (SerialBlob) sb.clone(); + InputStream is = sb2.getBinaryStream(1, 3); + }); } /* @@ -264,10 +311,12 @@ public class SerialBlobTests extends BaseTest { * Validate a SerialException is thrown if byte[] is used to * create the SeriablBlob and setBinaryStream is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test21() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - sb.setBinaryStream(3); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + sb.setBinaryStream(3); + }); } /* @@ -281,7 +330,7 @@ public class SerialBlobTests extends BaseTest { byte[] expected = new byte[]{1, 7, 8, 9, 5}; SerialBlob sb = new SerialBlob(bytes); int written = sb.setBytes(2, diff); - assertEquals(written, diff.length); + assertEquals(diff.length, written); assertTrue( Arrays.equals(sb.getBytes(1, (int) sb.length()), expected), @@ -300,7 +349,7 @@ public class SerialBlobTests extends BaseTest { byte[] expected = new byte[]{1, 8, 9, 0, 5}; SerialBlob sb = new SerialBlob(bytes); int written = sb.setBytes(2, diff, 1, bytesToWrite); - assertEquals(written, bytesToWrite); + assertEquals(bytesToWrite, written); assertTrue( Arrays.equals(sb.getBytes(1, (int) sb.length()), expected), @@ -355,7 +404,7 @@ public class SerialBlobTests extends BaseTest { byte[] pattern = new byte[]{3, 4}; SerialBlob sb = new SerialBlob(bytes); long pos = sb.position(pattern, 1); - assertEquals(pos, expectedPos); + assertEquals(expectedPos, pos); } /* @@ -368,7 +417,7 @@ public class SerialBlobTests extends BaseTest { byte[] pattern = new byte[]{3, 4, 5}; SerialBlob sb = new SerialBlob(bytes); long pos = sb.position(pattern, 2); - assertEquals(pos, expectedPos); + assertEquals(expectedPos, pos); } /* @@ -381,7 +430,7 @@ public class SerialBlobTests extends BaseTest { byte[] pattern = new byte[]{4, 6}; SerialBlob sb = new SerialBlob(new StubBlob()); long pos = sb.position(pattern, 1); - assertEquals(pos, expectedPos); + assertEquals(expectedPos, pos); } /* @@ -394,7 +443,7 @@ public class SerialBlobTests extends BaseTest { byte[] pattern = new byte[]{6, 8}; SerialBlob sb = new SerialBlob(new StubBlob()); long pos = sb.position(pattern, 2); - assertEquals(pos, expectedPos); + assertEquals(expectedPos, pos); } /* @@ -411,8 +460,8 @@ public class SerialBlobTests extends BaseTest { byte[] expected = new byte[]{1, 2, 3, 4, 7}; SerialBlob sb = new SerialBlob(bytes); int written = sb.setBytes(writePos, diff, 0, bytesToWrite); - assertEquals(written, bytesToWrite); - assertEquals(sb.getBytes(1, (int) sb.length()), expected); + assertEquals(bytesToWrite, written); + assertArrayEquals(expected, sb.getBytes(1, (int) sb.length())); } /* @@ -428,8 +477,8 @@ public class SerialBlobTests extends BaseTest { byte[] expected = new byte[]{1, 2, 3, 4, 8, 9}; SerialBlob sb = new SerialBlob(bytes); int written = sb.setBytes(writePos, diff, 1, bytesToWrite); - assertEquals(written, bytesToWrite); - assertEquals(sb.getBytes(1, (int) sb.length()), expected); + assertEquals(bytesToWrite, written); + assertArrayEquals(expected, sb.getBytes(1, (int) sb.length())); } /* @@ -445,29 +494,33 @@ public class SerialBlobTests extends BaseTest { byte[] expected = new byte[]{1, 2, 3, 4, 5, 8, 9, 0}; SerialBlob sb = new SerialBlob(bytes); int written = sb.setBytes(writePos, diff, 1, bytesToWrite); - assertEquals(written, bytesToWrite); - assertEquals(sb.getBytes(1, (int) sb.length()), expected); + assertEquals(bytesToWrite, written); + assertArrayEquals(expected, sb.getBytes(1, (int) sb.length())); } /* * Validate a SerialException is thrown if length < 0 for setBytes */ - @Test(expectedExceptions = SerialException.class) + @Test public void test34() throws Exception { - int length = -1; - SerialBlob sb = new SerialBlob(bytes); - int written = sb.setBytes(1, new byte[]{1}, 1, length); + assertThrows(SerialException.class, () -> { + int length = -1; + SerialBlob sb = new SerialBlob(bytes); + int written = sb.setBytes(1, new byte[]{1}, 1, length); + }); } /* * Validate a SerialException is thrown if length + offset > * Integer.MAX_VALUE for setBytes */ - @Test(expectedExceptions = SerialException.class) + @Test public void test35() throws Exception { - int offset = 1; - int length = Integer.MAX_VALUE; - SerialBlob sb = new SerialBlob(bytes); - int written = sb.setBytes(1, new byte[]{1, 2, 3}, offset, length); + assertThrows(SerialException.class, () -> { + int offset = 1; + int length = Integer.MAX_VALUE; + SerialBlob sb = new SerialBlob(bytes); + int written = sb.setBytes(1, new byte[]{1, 2, 3}, offset, length); + }); } } diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialClobTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialClobTests.java similarity index 72% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialClobTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialClobTests.java index 95c5f6f64a7..2636c3b6a84 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialClobTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialClobTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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,10 +26,14 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; +import java.sql.SQLException; import javax.sql.rowset.serial.SerialClob; import javax.sql.rowset.serial.SerialException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubClob; @@ -43,6 +47,26 @@ public class SerialClobTests extends BaseTest { 'o', 'r', 'l', 'd'}; } + /* + * Validate calling setAsciiStream() on a SerialClob constructed from a + * char array throws SerialException. Bug 7077451. + */ + @Test + void setAsciiStreamExceptionTest() throws SQLException { + SerialClob clob = new SerialClob(new char[0]); + assertThrows(SerialException.class, () -> clob.setAsciiStream(0)); + } + + /* + * Validate calling setCharacterStream() on a SerialClob constructed from a + * char array throws SerialException. Bug 7077451. + */ + @Test + void setCharacterStreamExceptionTest() throws SQLException { + SerialClob clob = new SerialClob(new char[0]); + assertThrows(SerialException.class, () -> clob.setCharacterStream(0)); + } + /* * Validate calling free() does not throw an Exception */ @@ -56,192 +80,228 @@ public class SerialClobTests extends BaseTest { * Validate calling getCharacterStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test01() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.getCharacterStream(); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.getCharacterStream(); + }); } /* * Validate calling getCharacterStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test02() throws Exception { - SerialClob sc = new SerialClob(chars); - sc.free(); - sc.getCharacterStream(); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(chars); + sc.free(); + sc.getCharacterStream(); + }); } /* * Validate calling getCharacterStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test03() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.getCharacterStream(1, 5); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.getCharacterStream(1, 5); + }); } /* * Validate calling getSubString() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test04() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.getSubString(1, 1); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.getSubString(1, 1); + }); } /* * Validate calling truncate() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test05() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.truncate(1); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.truncate(1); + }); } /* * Validate calling getAsciiStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test06() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.getAsciiStream(); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.getAsciiStream(); + }); } /* * Validate calling length() after calling free() throws an SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test07() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.length(); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.length(); + }); } /* * Validate calling position() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test08() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.position("hello", 1); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.position("hello", 1); + }); } /* * Validate calling position() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test09() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.position(new StubClob(), 1); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.position(new StubClob(), 1); + }); } /* * Validate calling setAsciiStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test10() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.setAsciiStream(5); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.setAsciiStream(5); + }); } /* * Validate calling setCharacterStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test11() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.setCharacterStream(5); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.setCharacterStream(5); + }); } /* * Validate calling setString() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test12() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.setString(1, "hello"); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.setString(1, "hello"); + }); } /* * Validate calling setString() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test13() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.setString(1, "hello", 0, 5); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.setString(1, "hello", 0, 5); + }); } /* * Test that SerialException is thrown if pos < 0 on a call to * getCharacterStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test14() throws Exception { - SerialClob sc = new SerialClob(chars); - sc.getCharacterStream(-1, 5); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(chars); + sc.getCharacterStream(-1, 5); + }); } /* * Test that SerialException is thrown if pos = 0 on a call to * getCharacterStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test15() throws Exception { - SerialClob sc = new SerialClob(chars); - sc.getCharacterStream(0, 5); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(chars); + sc.getCharacterStream(0, 5); + }); } /* * Test that SerialException is thrown if pos = 0 on a call to * getCharacterStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test16() throws Exception { - SerialClob sc = new SerialClob(chars); - sc.getCharacterStream(1, 100); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(chars); + sc.getCharacterStream(1, 100); + }); } /* * Test that SerialException is thrown if length = 0 on a call to * getCharacterStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test17() throws Exception { - SerialClob sc = new SerialClob(chars); - sc.getCharacterStream(1, 0); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(chars); + sc.getCharacterStream(1, 0); + }); } /* * Test that SerialException is thrown if pos > length on a call to * getCharacterStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test18() throws Exception { - SerialClob sc = new SerialClob(chars); - sc.getCharacterStream(100, 5); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(chars); + sc.getCharacterStream(100, 5); + }); } /* @@ -401,7 +461,7 @@ public class SerialClobTests extends BaseTest { String expected = "Hello, I am the Batman!"; SerialClob sc = new SerialClob(val.toCharArray()); int written = sc.setString(13, val1); - assertEquals(val1.length(), written); + assertEquals(written, val1.length()); assertTrue(expected.equals(sc.getSubString(1, (int) sc.length()))); } @@ -420,8 +480,8 @@ public class SerialClobTests extends BaseTest { String expected = "Hi, I am the Joker!!!!!"; SerialClob sc = new SerialClob(val.toCharArray()); int written = sc.setString(writePos, val1, offset, expectedWritten); - assertEquals(written, expectedWritten); - assertEquals(sc.getSubString(1, (int) sc.length()), expected); + assertEquals(expectedWritten, written); + assertEquals(expected, sc.getSubString(1, (int) sc.length())); } /* @@ -478,7 +538,8 @@ public class SerialClobTests extends BaseTest { * Check that getCharacterStream() returns a Reader and that the char[] that * was specified to create the SerialClob can be returned via the Reader */ - @Test(enabled = false) + @Test + @Disabled public void test37() throws Exception { SerialClob sc = new SerialClob(chars); String expected = "ello w"; @@ -504,12 +565,14 @@ public class SerialClobTests extends BaseTest { * Check calling setString() with offset > val1.length() throws a * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test39() throws Exception { - String val1 = "hello"; - int offset = val1.length() + 1; - SerialClob sc = new SerialClob(chars); - sc.setString(1, val1, offset, 0); + assertThrows(SerialException.class, () -> { + String val1 = "hello"; + int offset = val1.length() + 1; + SerialClob sc = new SerialClob(chars); + sc.setString(1, val1, offset, 0); + }); } /* @@ -526,8 +589,8 @@ public class SerialClobTests extends BaseTest { String expected = "Hello, I am the Joker, who are you?!"; SerialClob sc = new SerialClob(val.toCharArray()); int written = sc.setString(writePos, val1, offset, expectedWritten); - assertEquals(written, expectedWritten); - assertEquals(sc.getSubString(1, (int) sc.length()), expected); + assertEquals(expectedWritten, written); + assertEquals(expected, sc.getSubString(1, (int) sc.length())); } /* @@ -544,29 +607,33 @@ public class SerialClobTests extends BaseTest { String expected = "Hi, I am the Joker!"; SerialClob sc = new SerialClob(val.toCharArray()); int written = sc.setString(writePos, val1, offset, expectedWritten); - assertEquals(written, expectedWritten); - assertEquals(sc.getSubString(1, (int) sc.length()), expected); + assertEquals(expectedWritten, written); + assertEquals(expected, sc.getSubString(1, (int) sc.length())); } /* * Validate a SerialException is thrown if length < 0 for setString */ - @Test(expectedExceptions = SerialException.class) + @Test public void test42() throws Exception { - int length = -1; - SerialClob sc = new SerialClob(chars); - int written = sc.setString(1, "hello", 1, length); + assertThrows(SerialException.class, () -> { + int length = -1; + SerialClob sc = new SerialClob(chars); + int written = sc.setString(1, "hello", 1, length); + }); } /* * Validate a SerialException is thrown if length + offset > * Integer.MAX_VALUE for setString */ - @Test(expectedExceptions = SerialException.class) + @Test public void test43() throws Exception { - int offset = 1; - int length = Integer.MAX_VALUE; - SerialClob sc = new SerialClob(chars); - int written = sc.setString(1, "hello", offset, length); + assertThrows(SerialException.class, () -> { + int offset = 1; + int length = Integer.MAX_VALUE; + SerialClob sc = new SerialClob(chars); + int written = sc.setString(1, "hello", offset, length); + }); } } diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialDataLinkTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialDataLinkTests.java similarity index 90% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialDataLinkTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialDataLinkTests.java index 0c7f19e85b1..12c1341161d 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialDataLinkTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialDataLinkTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -25,9 +25,11 @@ package test.rowset.serial; import java.net.URL; import javax.sql.rowset.serial.SerialDatalink; import javax.sql.rowset.serial.SerialException; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SerialDataLinkTests extends BaseTest { @@ -36,7 +38,7 @@ public class SerialDataLinkTests extends BaseTest { private URL u1; private SerialDatalink dl; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { u = new URL("http://www.oracle.com/"); u1 = new URL("http://www.usatoday.com/"); @@ -46,9 +48,11 @@ public class SerialDataLinkTests extends BaseTest { /* * Validate that a SerialException is thrown if the URL is null */ - @Test(expectedExceptions = SerialException.class) + @Test public void test() throws Exception { - SerialDatalink dl1 = new SerialDatalink(null); + assertThrows(SerialException.class, () -> { + SerialDatalink dl1 = new SerialDatalink(null); + }); } /* diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialExceptionTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialExceptionTests.java similarity index 95% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialExceptionTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialExceptionTests.java index 8ef215584a9..51c733b995a 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialExceptionTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -24,8 +24,10 @@ package test.rowset.serial; import java.sql.SQLException; import javax.sql.rowset.serial.SerialException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SerialExceptionTests extends BaseTest { diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialJavaObjectTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialJavaObjectTests.java similarity index 86% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialJavaObjectTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialJavaObjectTests.java index c2fcf2fa48f..e521125df97 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialJavaObjectTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialJavaObjectTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -27,8 +27,11 @@ import java.util.Arrays; import javax.sql.rowset.RowSetMetaDataImpl; import javax.sql.rowset.serial.SerialException; import javax.sql.rowset.serial.SerialJavaObject; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SerialJavaObjectTests extends BaseTest { @@ -37,18 +40,23 @@ public class SerialJavaObjectTests extends BaseTest { * Validate that an NPE is thrown when null is specified to create * the SerialJavaObject */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test() throws Exception { - SerialJavaObject sjo = new SerialJavaObject(null); + assertThrows(NullPointerException.class, () -> { + SerialJavaObject sjo = new SerialJavaObject(null); + }); } /* * Validate that a SerialException is thrown when the object specified * contains public static fields */ - @Test(expectedExceptions = SerialException.class, enabled = false) + @Test + @Disabled public void test01() throws Exception { - SerialJavaObject sjo = new SerialJavaObject(new RowSetMetaDataImpl()); + assertThrows(SerialException.class, () -> { + SerialJavaObject sjo = new SerialJavaObject(new RowSetMetaDataImpl()); + }); } /* diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialRefTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialRefTests.java similarity index 86% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialRefTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialRefTests.java index 8f23de70aa6..1b4e72593b0 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialRefTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialRefTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -27,9 +27,12 @@ import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import javax.sql.rowset.serial.SerialRef; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubRef; import util.SuperHero; @@ -41,7 +44,7 @@ public class SerialRefTests extends BaseTest { private final String sqlType = "SUPERHERO"; private SuperHero hero; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { map.put(sqlType, Class.forName("util.SuperHero")); hero = new SuperHero(sqlType, "Bruce", "Wayne", 1939, "Batman"); @@ -51,18 +54,22 @@ public class SerialRefTests extends BaseTest { /* * Validate that a SQLException() is thrown if the Ref is null */ - @Test(expectedExceptions = SQLException.class) + @Test public void test01() throws Exception { - SerialRef sr = new SerialRef(null); + assertThrows(SQLException.class, () -> { + SerialRef sr = new SerialRef(null); + }); } /* * Validate that a SQLException() is thrown if the typeName is null in the * Ref used to create the SerialRef */ - @Test(expectedExceptions = SQLException.class) + @Test public void test02() throws Exception { - SerialRef sr = new SerialRef(new StubRef(null, hero)); + assertThrows(SQLException.class, () -> { + SerialRef sr = new SerialRef(new StubRef(null, hero)); + }); } /* @@ -72,7 +79,7 @@ public class SerialRefTests extends BaseTest { @Test public void test03() throws Exception { SerialRef sr = new SerialRef(ref); - assertEquals(sr.getBaseTypeName(), sqlType); + assertEquals(sqlType, sr.getBaseTypeName()); } /* @@ -87,7 +94,8 @@ public class SerialRefTests extends BaseTest { /* * Validate that getObject() returns the same object used to create the Ref */ - @Test(enabled = false) + @Test + @Disabled public void test05() throws Exception { SerialRef sr = new SerialRef(ref); assertTrue(hero.equals(sr.getObject(map))); diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialStructTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialStructTests.java similarity index 92% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialStructTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialStructTests.java index 79ac2421c9a..dfc34b60e2b 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialStructTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialStructTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -27,9 +27,11 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; import javax.sql.rowset.serial.SerialStruct; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubStruct; import util.SuperHero; @@ -42,7 +44,7 @@ public class SerialStructTests extends BaseTest { private final String sqlType = "SUPERHERO"; private SuperHero hero; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { attributes = new Object[]{"Bruce", "Wayne", 1939, "Batman"}; @@ -58,7 +60,7 @@ public class SerialStructTests extends BaseTest { @Test public void test01() throws Exception { SerialStruct ss = new SerialStruct(struct, map); - assertEquals(ss.getSQLTypeName(), sqlType); + assertEquals(sqlType, ss.getSQLTypeName()); } /* @@ -68,7 +70,7 @@ public class SerialStructTests extends BaseTest { @Test public void test02() throws Exception { SerialStruct ss = new SerialStruct(hero, map); - assertEquals(ss.getSQLTypeName(), sqlType); + assertEquals(sqlType, ss.getSQLTypeName()); } /* diff --git a/test/jdk/javax/sql/testng/test/rowset/spi/SyncFactoryExceptionTests.java b/test/jdk/javax/sql/test/rowset/spi/SyncFactoryExceptionTests.java similarity index 95% rename from test/jdk/javax/sql/testng/test/rowset/spi/SyncFactoryExceptionTests.java rename to test/jdk/javax/sql/test/rowset/spi/SyncFactoryExceptionTests.java index e93a6109ec7..b747c48989d 100644 --- a/test/jdk/javax/sql/testng/test/rowset/spi/SyncFactoryExceptionTests.java +++ b/test/jdk/javax/sql/test/rowset/spi/SyncFactoryExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -24,8 +24,10 @@ package test.rowset.spi; import java.sql.SQLException; import javax.sql.rowset.spi.SyncFactoryException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SyncFactoryExceptionTests extends BaseTest { diff --git a/test/jdk/javax/sql/testng/test/rowset/spi/SyncFactoryTests.java b/test/jdk/javax/sql/test/rowset/spi/SyncFactoryTests.java similarity index 92% rename from test/jdk/javax/sql/testng/test/rowset/spi/SyncFactoryTests.java rename to test/jdk/javax/sql/test/rowset/spi/SyncFactoryTests.java index 415488abb9f..260cf150f59 100644 --- a/test/jdk/javax/sql/testng/test/rowset/spi/SyncFactoryTests.java +++ b/test/jdk/javax/sql/test/rowset/spi/SyncFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -33,9 +33,11 @@ import javax.naming.Context; import javax.sql.rowset.spi.SyncFactory; import javax.sql.rowset.spi.SyncFactoryException; import javax.sql.rowset.spi.SyncProvider; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.PropertyStubProvider; import util.StubSyncProvider; import util.StubContext; @@ -67,7 +69,7 @@ public class SyncFactoryTests { ctx = new StubContext(); } - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { // Make sure the provider provider that is registered is removed // before each run @@ -120,17 +122,21 @@ public class SyncFactoryTests { /* * Validate that a SyncFactoryException is thrown if the ProviderID is null */ - @Test(expectedExceptions = SyncFactoryException.class) + @Test public void test03() throws SyncFactoryException { - SyncProvider p = SyncFactory.getInstance(null); + assertThrows(SyncFactoryException.class, () -> { + SyncProvider p = SyncFactory.getInstance(null); + }); } /* * Validate that a SyncFactoryException is thrown if the Logger is null */ - @Test(expectedExceptions = SyncFactoryException.class,enabled=true) + @Test public void test04() throws SyncFactoryException { - Logger l = SyncFactory.getLogger(); + assertThrows(SyncFactoryException.class, () -> { + Logger l = SyncFactory.getLogger(); + }); } /* @@ -179,15 +185,15 @@ public class SyncFactoryTests { * Validate that setJNDIContext throws a SyncFactoryException if the * context is null */ - @Test(expectedExceptions = SyncFactoryException.class, enabled=true) + @Test public void test08() throws Exception { - SyncFactory.setJNDIContext(null); + assertThrows(SyncFactoryException.class, () -> SyncFactory.setJNDIContext(null)); } /* * Validate that setJNDIContext succeeds */ - @Test(enabled=true) + @Test public void test09() throws Exception { SyncFactory.setJNDIContext(ctx); } diff --git a/test/jdk/javax/sql/testng/test/rowset/spi/SyncProviderExceptionTests.java b/test/jdk/javax/sql/test/rowset/spi/SyncProviderExceptionTests.java similarity index 96% rename from test/jdk/javax/sql/testng/test/rowset/spi/SyncProviderExceptionTests.java rename to test/jdk/javax/sql/test/rowset/spi/SyncProviderExceptionTests.java index 22863b0f6bb..f8786f1b429 100644 --- a/test/jdk/javax/sql/testng/test/rowset/spi/SyncProviderExceptionTests.java +++ b/test/jdk/javax/sql/test/rowset/spi/SyncProviderExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 @@ -27,11 +27,12 @@ import java.sql.SQLException; import javax.sql.rowset.spi.SyncProviderException; import javax.sql.rowset.spi.SyncResolver; -import static org.testng.Assert.*; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubSyncResolver; @@ -40,16 +41,16 @@ public class SyncProviderExceptionTests extends BaseTest { // Used by SyncProviderException::getSyncResolver tests private SyncResolver resolver; - @BeforeClass + @BeforeAll public static void setUpClass() throws Exception { System.out.println(System.getProperty("java.naming.factory.initial")); } - @AfterClass + @AfterAll public static void tearDownClass() throws Exception { } - @BeforeMethod + @BeforeEach public void setupTest() { resolver = new SyncProviderException().getSyncResolver(); } diff --git a/test/jdk/javax/sql/testng/test/rowset/webrowset/CommonWebRowSetTests.java b/test/jdk/javax/sql/test/rowset/webrowset/CommonWebRowSetTests.java similarity index 81% rename from test/jdk/javax/sql/testng/test/rowset/webrowset/CommonWebRowSetTests.java rename to test/jdk/javax/sql/test/rowset/webrowset/CommonWebRowSetTests.java index ce0801e0fe4..023adc8254b 100644 --- a/test/jdk/javax/sql/testng/test/rowset/webrowset/CommonWebRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/webrowset/CommonWebRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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,11 +35,15 @@ import java.math.BigDecimal; import java.sql.ResultSet; import java.util.Arrays; import javax.sql.rowset.WebRowSet; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertEqualsNoOrder; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + import test.rowset.cachedrowset.CommonCachedRowSetTests; public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { @@ -131,10 +135,11 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { /* * Validate the expected Rows are contained within the RowSet */ - @Test(dataProvider = "rowsetUsingCoffees") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void WebRowSetTest0000(WebRowSet wrs) throws Exception { - assertEquals(getPrimaryKeys(wrs), COFFEES_PRIMARY_KEYS); - assertEquals(wrs.size(), COFFEES_ROWS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs)); + assertEquals(COFFEES_ROWS, wrs.size()); wrs.close(); } @@ -142,14 +147,15 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * Validate the expected Rows are contained within the RowSet * populated by readXML(Reader) */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0001(WebRowSet wrs1) throws Exception { try (FileReader fr = new FileReader(COFFEE_ROWS_XML)) { wrs1.readXml(fr); } - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); - assertEquals(wrs1.size(), COFFEES_ROWS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); + assertEquals(COFFEES_ROWS, wrs1.size()); wrs1.close(); } @@ -158,13 +164,14 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * Validate the expected Rows are contained within the RowSet * populated by readXML(InputStream) */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0002(WebRowSet wrs1) throws Exception { try (FileInputStream fis = new FileInputStream(COFFEE_ROWS_XML)) { wrs1.readXml(fis); } - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); - assertEquals(wrs1.size(), COFFEES_ROWS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); + assertEquals(COFFEES_ROWS, wrs1.size()); wrs1.close(); } @@ -173,12 +180,13 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * back via readXML(InputStream) and validate the primary keys * are the same */ - @Test(dataProvider = "rowsetUsingCoffees") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void WebRowSetTest0003(WebRowSet wrs) throws Exception { ByteArrayOutputStream baos = writeWebRowSetWithOutputStream(wrs); try (WebRowSet wrs1 = readWebRowSetWithOInputStream(baos)) { - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); - assertEquals(wrs1.size(), COFFEES_ROWS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); + assertEquals(COFFEES_ROWS, wrs1.size()); } } @@ -187,14 +195,15 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * back via readXML(InputStream) and validate the primary keys * are the same */ - @Test(dataProvider = "rowsetUsingCoffees") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void WebRowSetTest0004(WebRowSet wrs) throws Exception { ResultSet rs = wrs; rs.beforeFirst(); ByteArrayOutputStream baos = writeWebRowSetWithOutputStream(rs); try (WebRowSet wrs1 = readWebRowSetWithOInputStream(baos)) { - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); - assertEquals(wrs1.size(), COFFEES_ROWS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); + assertEquals(COFFEES_ROWS, wrs1.size()); } } @@ -203,12 +212,13 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * back via readXML(Reader) and validate the primary keys * are the same */ - @Test(dataProvider = "rowsetUsingCoffees") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void WebRowSetTest0005(WebRowSet wrs) throws Exception { ByteArrayOutputStream baos = writeWebRowSetWithOutputStreamWithWriter(wrs); try (WebRowSet wrs1 = readWebRowSetWithOInputStreamWithReader(baos)) { - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); - assertEquals(wrs1.size(), COFFEES_ROWS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); + assertEquals(COFFEES_ROWS, wrs1.size()); } } @@ -217,14 +227,15 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * back via readXML(Reader) and validate the primary keys * are the same */ - @Test(dataProvider = "rowsetUsingCoffees") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void WebRowSetTest0006(WebRowSet wrs) throws Exception { ResultSet rs = wrs; rs.beforeFirst(); ByteArrayOutputStream baos = writeWebRowSetWithOutputStreamWithWriter(rs); try (WebRowSet wrs1 = readWebRowSetWithOInputStreamWithReader(baos)) { - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); - assertEquals(wrs1.size(), COFFEES_ROWS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); + assertEquals(COFFEES_ROWS, wrs1.size()); } } @@ -232,11 +243,13 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * Validate the expected Rows are contained within the RowSet * after deleting the specified rows */ - @Test(dataProvider = "rowsetUsingCoffees", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void WebRowSetTest0007(WebRowSet wrs) throws Exception { - assertEquals(getPrimaryKeys(wrs), COFFEES_PRIMARY_KEYS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs)); int[] rowsToDelete = {2, 4}; - assertEquals(getPrimaryKeys(wrs), COFFEES_PRIMARY_KEYS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs)); for (int row : rowsToDelete) { assertTrue(deleteRowByPrimaryKey(wrs, row, 1)); } @@ -262,12 +275,13 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * that was populated by reading an xml file with all rows * marked as a currentRow */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0008(WebRowSet wrs1) throws Exception { FileInputStream fis = new FileInputStream(COFFEE_ROWS_XML); wrs1.readXml(fis); assertTrue(wrs1.size() == COFFEES_ROWS); - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); // Validate that the rows are not marked as deleted, inserted or updated wrs1.beforeFirst(); while (wrs1.next()) { @@ -284,14 +298,15 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * Also validate that they are or are not visible based on the * setShowDeleted value */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0009(WebRowSet wrs1) throws Exception { int[] rowsToDelete = {2, 4}; Object[] expectedRows = {1, 3, 5}; FileInputStream fis = new FileInputStream(DELETED_COFFEE_ROWS_XML); wrs1.readXml(fis); assertTrue(wrs1.size() == COFFEES_ROWS); - assertEquals(getPrimaryKeys(wrs1), expectedRows); + assertArrayEquals(expectedRows, getPrimaryKeys(wrs1)); // With setShowDeleted(false) which is the default, // the deleted row should not be visible for (int row : rowsToDelete) { @@ -302,7 +317,7 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { for (int row : rowsToDelete) { assertTrue(findRowByPrimaryKey(wrs1, row, 1)); } - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); wrs1.close(); } @@ -311,12 +326,13 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * Validate that the correct row in the WebRowSet that had been created * from an xml file is marked as updated and contains the correct values */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0010(WebRowSet wrs1) throws Exception { FileInputStream fis = new FileInputStream(UPDATED_COFFEE_ROWS_XML); wrs1.readXml(fis); assertTrue(wrs1.size() == COFFEES_ROWS); - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); wrs1.beforeFirst(); while (wrs1.next()) { if (wrs1.getInt(1) == 3) { @@ -337,7 +353,8 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * Validate the correct row is marked as inserted in a WebRowSet * that is read from an xml file */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0011(WebRowSet wrs1) throws Exception { int expectedSize = COFFEES_ROWS + 2; int addedRowPK = 15; @@ -348,7 +365,10 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { FileInputStream fis = new FileInputStream(INSERTED_COFFEE_ROWS_XML); wrs1.readXml(fis); assertTrue(wrs1.size() == expectedSize); - assertEqualsNoOrder(getPrimaryKeys(wrs1), expected); + var actual = getPrimaryKeys(wrs1); + Arrays.sort(actual); + Arrays.sort(expected); + assertArrayEquals(expected, actual); wrs1.beforeFirst(); while (wrs1.next()) { if (wrs1.getInt(1) == 15 || wrs1.getInt(1) == 20) { @@ -367,7 +387,8 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { /* * Read an xml file which contains a row that was inserted and updated */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0012(WebRowSet wrs1) throws Exception { int expectedSize = COFFEES_ROWS + 1; int addedRowPK = 100; @@ -376,7 +397,7 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { FileInputStream fis = new FileInputStream(UPDATED_INSERTED_COFFEE_ROWS_XML); wrs1.readXml(fis); assertTrue(wrs1.size() == expectedSize); - assertEquals(getPrimaryKeys(wrs1), expected); + assertArrayEquals(expected, getPrimaryKeys(wrs1)); wrs1.beforeFirst(); while (wrs1.next()) { if (wrs1.getInt(1) == addedRowPK) { diff --git a/test/jdk/javax/sql/testng/test/rowset/webrowset/WebRowSetTests.java b/test/jdk/javax/sql/test/rowset/webrowset/WebRowSetTests.java similarity index 94% rename from test/jdk/javax/sql/testng/test/rowset/webrowset/WebRowSetTests.java rename to test/jdk/javax/sql/test/rowset/webrowset/WebRowSetTests.java index 5f2890e0b5b..4692c0cee84 100644 --- a/test/jdk/javax/sql/testng/test/rowset/webrowset/WebRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/webrowset/WebRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/PropertyStubProvider.java b/test/jdk/javax/sql/util/PropertyStubProvider.java similarity index 92% rename from test/jdk/javax/sql/testng/util/PropertyStubProvider.java rename to test/jdk/javax/sql/util/PropertyStubProvider.java index d397cc1c7d4..1cadcef26bb 100644 --- a/test/jdk/javax/sql/testng/util/PropertyStubProvider.java +++ b/test/jdk/javax/sql/util/PropertyStubProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubArray.java b/test/jdk/javax/sql/util/StubArray.java similarity index 97% rename from test/jdk/javax/sql/testng/util/StubArray.java rename to test/jdk/javax/sql/util/StubArray.java index 1d2ef0e5b6f..b57760679a0 100644 --- a/test/jdk/javax/sql/testng/util/StubArray.java +++ b/test/jdk/javax/sql/util/StubArray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubBaseRowSet.java b/test/jdk/javax/sql/util/StubBaseRowSet.java similarity index 99% rename from test/jdk/javax/sql/testng/util/StubBaseRowSet.java rename to test/jdk/javax/sql/util/StubBaseRowSet.java index e003b522ed9..94be7f8e348 100644 --- a/test/jdk/javax/sql/testng/util/StubBaseRowSet.java +++ b/test/jdk/javax/sql/util/StubBaseRowSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubBlob.java b/test/jdk/javax/sql/util/StubBlob.java similarity index 97% rename from test/jdk/javax/sql/testng/util/StubBlob.java rename to test/jdk/javax/sql/util/StubBlob.java index 727e8a926d5..f86fd540841 100644 --- a/test/jdk/javax/sql/testng/util/StubBlob.java +++ b/test/jdk/javax/sql/util/StubBlob.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubCachedRowSetImpl.java b/test/jdk/javax/sql/util/StubCachedRowSetImpl.java similarity index 99% rename from test/jdk/javax/sql/testng/util/StubCachedRowSetImpl.java rename to test/jdk/javax/sql/util/StubCachedRowSetImpl.java index 9000deb1b65..65e8169dbb6 100644 --- a/test/jdk/javax/sql/testng/util/StubCachedRowSetImpl.java +++ b/test/jdk/javax/sql/util/StubCachedRowSetImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubClob.java b/test/jdk/javax/sql/util/StubClob.java similarity index 97% rename from test/jdk/javax/sql/testng/util/StubClob.java rename to test/jdk/javax/sql/util/StubClob.java index cb1e0c0a62a..b0db14b3526 100644 --- a/test/jdk/javax/sql/testng/util/StubClob.java +++ b/test/jdk/javax/sql/util/StubClob.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubContext.java b/test/jdk/javax/sql/util/StubContext.java similarity index 98% rename from test/jdk/javax/sql/testng/util/StubContext.java rename to test/jdk/javax/sql/util/StubContext.java index af03534d991..6f03a1abd48 100644 --- a/test/jdk/javax/sql/testng/util/StubContext.java +++ b/test/jdk/javax/sql/util/StubContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubFilteredRowSetImpl.java b/test/jdk/javax/sql/util/StubFilteredRowSetImpl.java similarity index 99% rename from test/jdk/javax/sql/testng/util/StubFilteredRowSetImpl.java rename to test/jdk/javax/sql/util/StubFilteredRowSetImpl.java index 8fcbfa4c085..0b73abc6c01 100644 --- a/test/jdk/javax/sql/testng/util/StubFilteredRowSetImpl.java +++ b/test/jdk/javax/sql/util/StubFilteredRowSetImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubJdbcRowSetImpl.java b/test/jdk/javax/sql/util/StubJdbcRowSetImpl.java similarity index 99% rename from test/jdk/javax/sql/testng/util/StubJdbcRowSetImpl.java rename to test/jdk/javax/sql/util/StubJdbcRowSetImpl.java index ec59eb6c556..441b662ddc6 100644 --- a/test/jdk/javax/sql/testng/util/StubJdbcRowSetImpl.java +++ b/test/jdk/javax/sql/util/StubJdbcRowSetImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubJoinRowSetImpl.java b/test/jdk/javax/sql/util/StubJoinRowSetImpl.java similarity index 99% rename from test/jdk/javax/sql/testng/util/StubJoinRowSetImpl.java rename to test/jdk/javax/sql/util/StubJoinRowSetImpl.java index c7ece7cd523..1f506f830ef 100644 --- a/test/jdk/javax/sql/testng/util/StubJoinRowSetImpl.java +++ b/test/jdk/javax/sql/util/StubJoinRowSetImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubNClob.java b/test/jdk/javax/sql/util/StubNClob.java similarity index 93% rename from test/jdk/javax/sql/testng/util/StubNClob.java rename to test/jdk/javax/sql/util/StubNClob.java index 10682578b9a..0664aa42795 100644 --- a/test/jdk/javax/sql/testng/util/StubNClob.java +++ b/test/jdk/javax/sql/util/StubNClob.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubRef.java b/test/jdk/javax/sql/util/StubRef.java similarity index 95% rename from test/jdk/javax/sql/testng/util/StubRef.java rename to test/jdk/javax/sql/util/StubRef.java index 052bd92c740..54a42df1e1c 100644 --- a/test/jdk/javax/sql/testng/util/StubRef.java +++ b/test/jdk/javax/sql/util/StubRef.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubRowId.java b/test/jdk/javax/sql/util/StubRowId.java similarity index 93% rename from test/jdk/javax/sql/testng/util/StubRowId.java rename to test/jdk/javax/sql/util/StubRowId.java index f8e90d49867..d3bc51b9320 100644 --- a/test/jdk/javax/sql/testng/util/StubRowId.java +++ b/test/jdk/javax/sql/util/StubRowId.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubRowSetFactory.java b/test/jdk/javax/sql/util/StubRowSetFactory.java similarity index 96% rename from test/jdk/javax/sql/testng/util/StubRowSetFactory.java rename to test/jdk/javax/sql/util/StubRowSetFactory.java index 624c53af1e7..abc34894a18 100644 --- a/test/jdk/javax/sql/testng/util/StubRowSetFactory.java +++ b/test/jdk/javax/sql/util/StubRowSetFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubSQLXML.java b/test/jdk/javax/sql/util/StubSQLXML.java similarity index 97% rename from test/jdk/javax/sql/testng/util/StubSQLXML.java rename to test/jdk/javax/sql/util/StubSQLXML.java index 44d1351b357..5e4d1647853 100644 --- a/test/jdk/javax/sql/testng/util/StubSQLXML.java +++ b/test/jdk/javax/sql/util/StubSQLXML.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubStruct.java b/test/jdk/javax/sql/util/StubStruct.java similarity index 95% rename from test/jdk/javax/sql/testng/util/StubStruct.java rename to test/jdk/javax/sql/util/StubStruct.java index 1dee8028a2c..a5a2004a09d 100644 --- a/test/jdk/javax/sql/testng/util/StubStruct.java +++ b/test/jdk/javax/sql/util/StubStruct.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubSyncProvider.java b/test/jdk/javax/sql/util/StubSyncProvider.java similarity index 97% rename from test/jdk/javax/sql/testng/util/StubSyncProvider.java rename to test/jdk/javax/sql/util/StubSyncProvider.java index 8947455c701..a6a44e0f509 100644 --- a/test/jdk/javax/sql/testng/util/StubSyncProvider.java +++ b/test/jdk/javax/sql/util/StubSyncProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubSyncResolver.java b/test/jdk/javax/sql/util/StubSyncResolver.java similarity index 99% rename from test/jdk/javax/sql/testng/util/StubSyncResolver.java rename to test/jdk/javax/sql/util/StubSyncResolver.java index 02477c7f9be..f24c3ec4b13 100644 --- a/test/jdk/javax/sql/testng/util/StubSyncResolver.java +++ b/test/jdk/javax/sql/util/StubSyncResolver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/StubWebRowSetImpl.java b/test/jdk/javax/sql/util/StubWebRowSetImpl.java similarity index 99% rename from test/jdk/javax/sql/testng/util/StubWebRowSetImpl.java rename to test/jdk/javax/sql/util/StubWebRowSetImpl.java index 918d5bd0482..48c237e73ae 100644 --- a/test/jdk/javax/sql/testng/util/StubWebRowSetImpl.java +++ b/test/jdk/javax/sql/util/StubWebRowSetImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/SuperHero.java b/test/jdk/javax/sql/util/SuperHero.java similarity index 97% rename from test/jdk/javax/sql/testng/util/SuperHero.java rename to test/jdk/javax/sql/util/SuperHero.java index c8afb3831de..2d6e68a7c9e 100644 --- a/test/jdk/javax/sql/testng/util/SuperHero.java +++ b/test/jdk/javax/sql/util/SuperHero.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/TestRowSetListener.java b/test/jdk/javax/sql/util/TestRowSetListener.java similarity index 96% rename from test/jdk/javax/sql/testng/util/TestRowSetListener.java rename to test/jdk/javax/sql/util/TestRowSetListener.java index 069ade29ca2..4f71f12da2e 100644 --- a/test/jdk/javax/sql/testng/util/TestRowSetListener.java +++ b/test/jdk/javax/sql/util/TestRowSetListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/util/TestSQLDataImpl.java b/test/jdk/javax/sql/util/TestSQLDataImpl.java similarity index 98% rename from test/jdk/javax/sql/testng/util/TestSQLDataImpl.java rename to test/jdk/javax/sql/util/TestSQLDataImpl.java index 0694add3ef9..f02eeb6fece 100644 --- a/test/jdk/javax/sql/testng/util/TestSQLDataImpl.java +++ b/test/jdk/javax/sql/util/TestSQLDataImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2026, 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 diff --git a/test/jdk/javax/sql/testng/xml/COFFEE_ROWS.xml b/test/jdk/javax/sql/xml/COFFEE_ROWS.xml similarity index 100% rename from test/jdk/javax/sql/testng/xml/COFFEE_ROWS.xml rename to test/jdk/javax/sql/xml/COFFEE_ROWS.xml diff --git a/test/jdk/javax/sql/testng/xml/DELETED_COFFEE_ROWS.xml b/test/jdk/javax/sql/xml/DELETED_COFFEE_ROWS.xml similarity index 100% rename from test/jdk/javax/sql/testng/xml/DELETED_COFFEE_ROWS.xml rename to test/jdk/javax/sql/xml/DELETED_COFFEE_ROWS.xml diff --git a/test/jdk/javax/sql/testng/xml/INSERTED_COFFEE_ROWS.xml b/test/jdk/javax/sql/xml/INSERTED_COFFEE_ROWS.xml similarity index 100% rename from test/jdk/javax/sql/testng/xml/INSERTED_COFFEE_ROWS.xml rename to test/jdk/javax/sql/xml/INSERTED_COFFEE_ROWS.xml diff --git a/test/jdk/javax/sql/testng/xml/MODFIED_DELETED_COFFEE_ROWS.xml b/test/jdk/javax/sql/xml/MODFIED_DELETED_COFFEE_ROWS.xml similarity index 100% rename from test/jdk/javax/sql/testng/xml/MODFIED_DELETED_COFFEE_ROWS.xml rename to test/jdk/javax/sql/xml/MODFIED_DELETED_COFFEE_ROWS.xml diff --git a/test/jdk/javax/sql/testng/xml/UPDATED_COFFEE_ROWS.xml b/test/jdk/javax/sql/xml/UPDATED_COFFEE_ROWS.xml similarity index 100% rename from test/jdk/javax/sql/testng/xml/UPDATED_COFFEE_ROWS.xml rename to test/jdk/javax/sql/xml/UPDATED_COFFEE_ROWS.xml diff --git a/test/jdk/javax/sql/testng/xml/UPDATED_INSERTED_COFFEE_ROWS.xml b/test/jdk/javax/sql/xml/UPDATED_INSERTED_COFFEE_ROWS.xml similarity index 100% rename from test/jdk/javax/sql/testng/xml/UPDATED_INSERTED_COFFEE_ROWS.xml rename to test/jdk/javax/sql/xml/UPDATED_INSERTED_COFFEE_ROWS.xml diff --git a/test/jdk/javax/swing/JFileChooser/4966171/bug4966171.java b/test/jdk/javax/swing/JFileChooser/4966171/bug4966171.java index f3b3ba684e6..27f26d570be 100644 --- a/test/jdk/javax/swing/JFileChooser/4966171/bug4966171.java +++ b/test/jdk/javax/swing/JFileChooser/4966171/bug4966171.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2026, 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,8 +50,8 @@ public final class bug4966171 { } private static void test() { - // Will run the test no more than 10 seconds per L&F - long endtime = System.nanoTime() + TimeUnit.SECONDS.toNanos(10); + // Will run the test no more than 5 seconds per L&F + long endtime = System.nanoTime() + TimeUnit.SECONDS.toNanos(5); while (System.nanoTime() < endtime) { try { var byteOut = new ByteArrayOutputStream(); diff --git a/test/jdk/javax/swing/JMenuItem/LargeMenuTextArrowIconPosition.java b/test/jdk/javax/swing/JMenuItem/LargeMenuTextArrowIconPosition.java new file mode 100644 index 00000000000..72512560cff --- /dev/null +++ b/test/jdk/javax/swing/JMenuItem/LargeMenuTextArrowIconPosition.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2026, 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. + * + * 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. + */ + +/* + * @test + * @bug 8374506 + * @summary Verify if arrow icon positioning is correct in + * parent JMenu in Windows L&F + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual LargeMenuTextArrowIconPosition + */ + +import java.awt.BorderLayout; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.UIManager; + +public class LargeMenuTextArrowIconPosition { + + private static final String INSTRUCTIONS = """ + A frame will be shown with a label. + Right click on the label. + + Check the arrow icon at the end of + "Really long Menu-Text" text. + If it overlaps with the menu text, + press Fail else press Pass."""; + + public static void main(String[] args) throws Throwable { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(LargeMenuTextArrowIconPosition::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + + JFrame frame = new JFrame("LargeMenuTextArrowIcon"); + frame.setSize(300, 150); + frame.setLayout(new BorderLayout()); + + JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.add(new JCheckBoxMenuItem("CheckBox On", true)); + popupMenu.add(new JCheckBoxMenuItem("CheckBox Icon On", + UIManager.getIcon("FileView.floppyDriveIcon"), true)); + popupMenu.add(new JCheckBoxMenuItem("CheckBox Icon Off", + UIManager.getIcon("FileView.floppyDriveIcon"), false)); + + JMenu menu = new JMenu("Really long Menu-Text"); + menu.add(new JMenuItem("Sub-MenuItem")); + menu.add(new JCheckBoxMenuItem("Sub-CheckBox On", true)); + + popupMenu.add(menu); + + JLabel lbl = new JLabel("Right click to invoke popupMenu"); + lbl.setComponentPopupMenu(popupMenu); + frame.add(lbl, BorderLayout.CENTER); + + return frame; + } + +} diff --git a/test/jdk/javax/swing/JPopupMenu/6495920/bug6495920.java b/test/jdk/javax/swing/JPopupMenu/6495920/bug6495920.java index 6a8c132c4e4..cc37cb97b18 100644 --- a/test/jdk/javax/swing/JPopupMenu/6495920/bug6495920.java +++ b/test/jdk/javax/swing/JPopupMenu/6495920/bug6495920.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2026, 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,12 +29,9 @@ interaction with GNOME is not crippled * @author Sergey Malenkov * @library ../.. - * @modules java.desktop/sun.awt * @modules java.desktop/javax.swing.plaf.basic:open */ -import sun.awt.AppContext; - import java.awt.Point; import java.awt.Robot; import java.awt.event.InputEvent; @@ -92,12 +89,12 @@ public class bug6495920 implements Thread.UncaughtExceptionHandler { } public void thirdValidate() throws Exception { - Field key = BasicPopupMenuUI.class.getDeclaredField("MOUSE_GRABBER_KEY"); + Field key = BasicPopupMenuUI.class.getDeclaredField("mouseGrabber"); key.setAccessible(true); - Object grabber = AppContext.getAppContext().get(key.get(null)); + Object grabber = key.get(null); if (grabber == null) { - throw new Exception("cannot find a mouse grabber in app's context"); + throw new Exception("cannot find a mouse grabber"); } Field field = grabber.getClass().getDeclaredField("grabbedWindow"); diff --git a/test/jdk/javax/swing/JPopupMenu/TestPopupInvoker.java b/test/jdk/javax/swing/JPopupMenu/TestPopupInvoker.java index c8ddfe67999..a3dc658df0e 100644 --- a/test/jdk/javax/swing/JPopupMenu/TestPopupInvoker.java +++ b/test/jdk/javax/swing/JPopupMenu/TestPopupInvoker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -23,13 +23,14 @@ /* * @test - * @bug 4938801 + * @bug 4938801 8376169 * @key headful * @summary Verifies popup is removed when the component is removed * @run main TestPopupInvoker */ import java.awt.BorderLayout; +import java.awt.Component; import java.awt.Container; import java.awt.Robot; import java.util.concurrent.CountDownLatch; @@ -48,6 +49,7 @@ public class TestPopupInvoker { static JFrame frame; static JLabel label; static Container pane; + static volatile Component invoker; private static final CountDownLatch popupShown = new CountDownLatch(1); private static final CountDownLatch popupHidden = new CountDownLatch(1); @@ -73,6 +75,7 @@ public class TestPopupInvoker { @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { popupHidden.countDown(); + popupMenu.setInvoker(null); } @Override @@ -106,6 +109,11 @@ public class TestPopupInvoker { if (!popupHidden.await(1, SECONDS)) { throw new RuntimeException("Popup is visible after component is removed"); } + + SwingUtilities.invokeAndWait(() -> invoker = popupMenu.getInvoker()); + if (invoker != null) { + throw new RuntimeException("Invoker is not null"); + } } finally { SwingUtilities.invokeAndWait(() -> { if (frame != null) { diff --git a/test/jdk/javax/swing/JTable/TestJTableColWidth.java b/test/jdk/javax/swing/JTable/TestJTableColWidth.java new file mode 100644 index 00000000000..542c4d0ffa1 --- /dev/null +++ b/test/jdk/javax/swing/JTable/TestJTableColWidth.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2026, 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. + * + * 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. + */ + +/* + * @test + * @bug 8192888 + * @key headful + * @summary Verifies JTable doesn't ignore setPreferredWidth during + * initial layout when AUTO_RESIZE_LAST_COLUMN is enabled + * @run main TestJTableColWidth + */ + +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableColumnModel; +import javax.swing.SwingUtilities; + +public class TestJTableColWidth { + static JFrame frame; + + public static void main(String[] args) throws Exception { + SwingUtilities.invokeAndWait(() -> { + try { + frame = new JFrame("JTable colwidth"); + + String[] cols = {"ID", "Name", "Description", "Status"}; + Object[][] data = {{1, "Mimi", "Testing Java 25 Regression", "Pending"}}; + + DefaultTableModel model = new DefaultTableModel(data, cols); + final JTable tab = new JTable(model); + + tab.getTableHeader().setReorderingAllowed(false); + tab.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); + + TableColumnModel columnModel = tab.getColumnModel(); + // Defined widths + int[] widths = {30, 200, 100, 50}; + + for (int i = 0; i < widths.length; i++) { + columnModel.getColumn(i).setPreferredWidth(widths[i]); + } + + frame.add(new JScrollPane(tab)); + frame.setSize(600, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + + System.out.println("Actual column widths on screen:"); + for (int i = 0; i < columnModel.getColumnCount(); i++) { + System.out.println("Column " + i + ": " + + columnModel.getColumn(i).getWidth() + "px"); + } + if (columnModel.getColumn(0).getWidth() + == columnModel.getColumn(1).getWidth()) { + throw new RuntimeException("JTable ignores setPreferredWidth during" + + " initial layout when AUTO_RESIZE_LAST_COLUMN is enabled"); + } + } finally { + if (frame != null) { + frame.dispose(); + } + } + }); + } +} diff --git a/test/jdk/javax/swing/UIManager/Test6657026.java b/test/jdk/javax/swing/UIManager/Test6657026.java deleted file mode 100644 index 5ce1ea73de4..00000000000 --- a/test/jdk/javax/swing/UIManager/Test6657026.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2009, 2015, 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. - * - * 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. - */ - -/* - * @test - * @bug 6657026 - * @summary Tests shared UIManager in different application contexts - * @author Sergey Malenkov - * @modules java.desktop/sun.awt - */ - -import sun.awt.SunToolkit; - -import javax.swing.UIManager; -import javax.swing.UIManager.LookAndFeelInfo; - -public class Test6657026 implements Runnable { - - public static void main(String[] args) throws Exception { - if (UIManager.getInstalledLookAndFeels().length == 0) { - throw new Error("unexpected amount of look&feels"); - } - UIManager.setInstalledLookAndFeels(new LookAndFeelInfo[0]); - if (UIManager.getInstalledLookAndFeels().length != 0) { - throw new Error("unexpected amount of look&feels"); - } - - ThreadGroup group = new ThreadGroup("$$$"); - Thread thread = new Thread(group, new Test6657026()); - thread.start(); - thread.join(); - } - - public void run() { - SunToolkit.createNewAppContext(); - if (UIManager.getInstalledLookAndFeels().length == 0) { - throw new Error("shared look&feels"); - } - } -} diff --git a/test/jdk/javax/swing/plaf/metal/MetalUtils/bug6190373.java b/test/jdk/javax/swing/plaf/metal/MetalUtils/bug6190373.java deleted file mode 100644 index 07211c7a569..00000000000 --- a/test/jdk/javax/swing/plaf/metal/MetalUtils/bug6190373.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2005, 2018, 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. - * - * 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. - */ - -import java.awt.Graphics; -import java.awt.image.BufferedImage; -import java.util.concurrent.CyclicBarrier; - -import javax.swing.JButton; -import javax.swing.SwingUtilities; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; - -import sun.awt.AppContext; -import sun.awt.SunToolkit; - -import static javax.swing.UIManager.getInstalledLookAndFeels; - -/** - * @test - * @bug 6190373 - * @summary Tests 6190373 - * @author Scott Violet - * @modules java.desktop/sun.awt - */ -public final class bug6190373 { - - private static AppContext app1; - private static AppContext app2; - private static final int LOOP_COUNT = 10000; - private static final CyclicBarrier barrier = new CyclicBarrier(2); - - public static void main(final String[] args) throws Exception { - final Thread t1 = new Thread(new ThreadGroup("firstGroup"), () -> { - app1 = SunToolkit.createNewAppContext(); - test(true); - }); - final Thread t2 = new Thread(new ThreadGroup("secondGroup"), () -> { - app2 = SunToolkit.createNewAppContext(); - test(false); - }); - - t1.start(); - t2.start(); - t1.join(); - t2.join(); - app1.dispose(); - app2.dispose(); - } - - private static void test(final boolean lock) { - for (final UIManager.LookAndFeelInfo laf : getInstalledLookAndFeels()) { - try { - SwingUtilities.invokeAndWait(() -> setLookAndFeel(laf)); - barrier.await(); - SwingUtilities.invokeAndWait(() -> slam(lock)); - barrier.await(); - } catch (final Exception e) { - throw new RuntimeException(e); - } - } - } - - private static void slam(final boolean lock) { - JButton button = new JButton("HI"); - button.setSize(100, 100); - BufferedImage image = new BufferedImage(100, 100, - BufferedImage.TYPE_INT_RGB); - for (int i = 0; i < LOOP_COUNT; i++) { - Graphics g = image.getGraphics(); - if (lock) { - synchronized (button.getTreeLock()) { - button.paint(g); - } - } else { - button.paint(g); - } - g.dispose(); - } - } - - private static void setLookAndFeel(final UIManager.LookAndFeelInfo laf) { - try { - UIManager.setLookAndFeel(laf.getClassName()); - System.out.println("LookAndFeel: " + laf.getClassName()); - } catch (final UnsupportedLookAndFeelException ignored){ - System.out.println("Unsupported LookAndFeel: " + laf.getClassName()); - } catch (ClassNotFoundException | InstantiationException | - IllegalAccessException e) { - throw new RuntimeException(e); - } - } -} diff --git a/test/jdk/javax/swing/plaf/synth/7143614/bug7143614.java b/test/jdk/javax/swing/plaf/synth/7143614/bug7143614.java deleted file mode 100644 index f1e1cbb94aa..00000000000 --- a/test/jdk/javax/swing/plaf/synth/7143614/bug7143614.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2012, 2015, 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. - * - * 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. - */ - -/* - * @test - * @bug 7143614 - * @summary Issues with Synth Look&Feel - * @author Pavel Porvatov - * @modules java.desktop/javax.swing.plaf.synth:open - * @modules java.desktop/sun.awt - */ - -import sun.awt.SunToolkit; - -import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.basic.BasicButtonUI; -import javax.swing.plaf.synth.SynthConstants; -import javax.swing.plaf.synth.SynthLookAndFeel; -import java.lang.reflect.Method; - -public class bug7143614 { - private static Method setSelectedUIMethod; - - private static ComponentUI componentUI = new BasicButtonUI(); - - public static void main(String[] args) throws Exception { - setSelectedUIMethod = SynthLookAndFeel.class.getDeclaredMethod("setSelectedUI", ComponentUI.class, - boolean.class, boolean.class, boolean.class, boolean.class); - setSelectedUIMethod.setAccessible(true); - - setSelectedUIMethod.invoke(null, componentUI, true, true, true, true); - - validate(); - - Thread thread = new ThreadInAnotherAppContext(); - - thread.start(); - thread.join(); - - validate(); - - System.out.println("Test bug7143614 passed."); - } - - private static void validate() throws Exception { - Method getSelectedUIMethod = SynthLookAndFeel.class.getDeclaredMethod("getSelectedUI"); - - getSelectedUIMethod.setAccessible(true); - - Method getSelectedUIStateMethod = SynthLookAndFeel.class.getDeclaredMethod("getSelectedUIState"); - - getSelectedUIStateMethod.setAccessible(true); - - if (getSelectedUIMethod.invoke(null) != componentUI) { - throw new RuntimeException("getSelectedUI returns invalid value"); - } - if (((Integer) getSelectedUIStateMethod.invoke(null)).intValue() != - (SynthConstants.SELECTED | SynthConstants.FOCUSED)) { - throw new RuntimeException("getSelectedUIState returns invalid value"); - } - - } - - private static class ThreadInAnotherAppContext extends Thread { - public ThreadInAnotherAppContext() { - super(new ThreadGroup("7143614"), "ThreadInAnotherAppContext"); - } - - public void run() { - SunToolkit.createNewAppContext(); - - try { - setSelectedUIMethod.invoke(null, null, false, false, false, false); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } -} diff --git a/test/jdk/javax/swing/plaf/synth/Test6660049.java b/test/jdk/javax/swing/plaf/synth/Test6660049.java deleted file mode 100644 index a7486eb86c8..00000000000 --- a/test/jdk/javax/swing/plaf/synth/Test6660049.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2009, 2016, 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. - * - * 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. - */ - -/* - * @test - * @key headful - * @bug 6660049 6849518 - * @summary Tests the Region initialization - * @author Sergey Malenkov - * @modules java.desktop/sun.awt - */ - -import sun.awt.SunToolkit; - -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.SwingUtilities; -import javax.swing.plaf.synth.Region; -import javax.swing.plaf.synth.SynthLookAndFeel; - -public class Test6660049 implements Runnable { - public static void main(String[] args) { - SwingUtilities.invokeLater(new Test6660049( - javax.swing.JButton.class, - javax.swing.JCheckBox.class, - javax.swing.JCheckBoxMenuItem.class, - javax.swing.JColorChooser.class, - javax.swing.JComboBox.class, - javax.swing.JDesktopPane.class, - javax.swing.JEditorPane.class, - javax.swing.JFileChooser.class, - javax.swing.JFormattedTextField.class, - javax.swing.JInternalFrame.class, - javax.swing.JLabel.class, - javax.swing.JList.class, - javax.swing.JMenu.class, - javax.swing.JMenuBar.class, - javax.swing.JMenuItem.class, - javax.swing.JOptionPane.class, - javax.swing.JPanel.class, - javax.swing.JPasswordField.class, - javax.swing.JPopupMenu.class, - javax.swing.JProgressBar.class, - javax.swing.JRadioButton.class, - javax.swing.JRadioButtonMenuItem.class, - javax.swing.JRootPane.class, - javax.swing.JScrollBar.class, - javax.swing.JScrollPane.class, - javax.swing.JSeparator.class, - javax.swing.JSlider.class, - javax.swing.JSpinner.class, - javax.swing.JSplitPane.class, - javax.swing.JTabbedPane.class, - javax.swing.JTable.class, - javax.swing.JTextArea.class, - javax.swing.JTextField.class, - javax.swing.JTextPane.class, - javax.swing.JToggleButton.class, - javax.swing.JToolBar.class, - javax.swing.JToolTip.class, - javax.swing.JTree.class, - javax.swing.JViewport.class, - javax.swing.table.JTableHeader.class)); - } - - private final Class[] types; - private final Region region; - - private Test6660049(Class... types) { - this.types = types; - run(); - - this.region = new Region("Button", "ButtonUI", true) { - @Override - public String getName() { - throw new Error("6660049: exploit is available"); - } - }; - } - - public void run() { - if (this.region != null) { - SunToolkit.createNewAppContext(); - } - for (Class type : this.types) { - Region region = getRegion(type); - if (region == null) { - throw new Error("6849518: region is not initialized"); - } - } - getRegion(JButton.class).getName(); - } - - private static Region getRegion(Class type) { - try { - return SynthLookAndFeel.getRegion(type.newInstance()); - } - catch (IllegalAccessException exception) { - throw new Error("unexpected exception", exception); - } - catch (InstantiationException exception) { - throw new Error("unexpected exception", exception); - } - } -} diff --git a/test/jdk/jdk/internal/loader/URLClassPath/JarManifestClassPathOrder.java b/test/jdk/jdk/internal/loader/URLClassPath/JarManifestClassPathOrder.java new file mode 100644 index 00000000000..c1cda1d448a --- /dev/null +++ b/test/jdk/jdk/internal/loader/URLClassPath/JarManifestClassPathOrder.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2026, 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. + * + * 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. + */ + + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/* + * @test + * @summary Verify that URLClassPath discovers JAR Class-Path URLs in the expected order + * @run junit JarManifestClassPathOrder + */ +public class JarManifestClassPathOrder { + + // Name of the JAR resource use for lookups + private static final String ENTRY_NAME = "JarClassPathOrdering.txt"; + + /** + * Verify that URLClassPath discovers JAR files in the expected order when + * only root JARs are on the 'original' class path and the other JARs are + * found via multiple levels of Class-Path Manifest attributes. + * + * @throws IOException if an unexpected error occurs + */ + @Test + public void shouldLoadJARsInExpectedOrder() throws IOException { + + // Set up a 'Class-Path' JAR tree of depth 3 + JarTree specs = new JarTree(); + specs.addJars(3); + + // List of "root" JAR file URLs to use for URLClassLoader + List searchPath = new ArrayList<>(); + + // Order of JAR files we expect URLClassPath to find after a DFS search + List expectedJarNames = new ArrayList<>(); + + for (JarSpec spec : specs.dfs) { + Path jar = createJar(spec); + expectedJarNames.add(jar.getFileName().toString()); + // Only root JARs in the search path, others are discovered transitively via "Class-Path" + if (spec.isRoot()) { + searchPath.add(jar.toUri().toURL()); + } + } + + // Load ENTRY_NAME to identify all JARs found by URLClassPath + try (URLClassLoader loader = new URLClassLoader(searchPath.toArray(new URL[0]))) { + Enumeration resources = loader.getResources(ENTRY_NAME); + // Collect all JAR file names in the order discovered by URLClassPath + List actualJarNames = Collections.list(resources) + .stream() + .map(this::extractJarName) + .collect(Collectors.toList()); + // JARs should be found in DFS order + assertEquals(expectedJarNames, actualJarNames, "JAR files not found in expected order"); + } + } + + // Extract file name of JAR file from a JAR URL + private String extractJarName(URL url) { + String jarPath = url.getPath().substring(0, url.getPath().indexOf("!/")); + String jarName = jarPath.substring(jarPath.lastIndexOf('/') + 1); + return jarName; + } + + // Create a JAR file according to the spec, possibly including a Class-Path attribute + private Path createJar(JarSpec spec) throws IOException { + Path file = Path.of(spec.name +".jar"); + Manifest man = new Manifest(); + Attributes attrs = man.getMainAttributes(); + attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0"); + + // Set Class-Path attribute + if (!spec.children.isEmpty()) { + String path = String.join(" ", spec.children + .stream() + .map(js -> js.name +".jar") + .collect(Collectors.toList())); + attrs.put(Attributes.Name.CLASS_PATH, path); + } + + try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(file), man)) { + // All JARs include the same entry + out.putNextEntry(new JarEntry(ENTRY_NAME)); + } + + return file; + } + + + // Helper class to represent a tree of JARs related by "Class-Path" + static class JarTree { + static final List NAMES = List.of("a", "b", "c"); + List dfs = new ArrayList<>(); + + private void addJar(String prefix, String name, JarSpec parent, int depth, int maxDepth) { + if (depth > maxDepth) { + return; + } + JarSpec spec = new JarSpec(prefix + name, parent); + dfs.add(spec); + + if (parent != null) { + parent.children.add(spec); + } + + for (String childName : NAMES) { + addJar(prefix + name, childName, spec, depth + 1, maxDepth); + } + } + + /* Make a tree of JARs related by the 'Class-Path' Manifest attribute: + * a.jar + * aa.jar + * aaa.jar + * aab.jar + * [...] + * ccc.jar + */ + public void addJars(int maxDepth) { + for (String name : NAMES) { + addJar("", name, null, 1, maxDepth); + } + } + } + + // Helper class to represent a JAR file to be found by URLClassPath + static class JarSpec { + final String name; + final JarSpec parent; + final List children = new ArrayList<>(); + + JarSpec(String name, JarSpec parent) { + this.name = name; + this.parent = parent; + } + boolean isRoot() { + return parent == null; + } + } +} diff --git a/test/jdk/tools/jar/ValidatorTest.java b/test/jdk/tools/jar/ValidatorTest.java index c2dfeae8dec..e3f649a04a2 100644 --- a/test/jdk/tools/jar/ValidatorTest.java +++ b/test/jdk/tools/jar/ValidatorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -23,12 +23,8 @@ /* * @test - * @bug 8345431 + * @bug 8345431 8375433 * @summary test validator to report malformed jar file - * @library /test/lib - * @modules jdk.jartool - * @build jdk.test.lib.Platform - * jdk.test.lib.util.FileUtils * @run junit/othervm ValidatorTest */ @@ -40,6 +36,7 @@ import org.junit.jupiter.api.TestInstance.Lifecycle; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertLinesMatch; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -59,13 +56,15 @@ import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import jdk.test.lib.util.FileUtils; - class ValidatorTest { private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar") .orElseThrow(() -> new RuntimeException("jar tool not found") ); + private static final ToolProvider JAVAC_TOOL = ToolProvider.findFirst("javac") + .orElseThrow(() -> + new RuntimeException("javac tool not found") + ); private final String nl = System.lineSeparator(); private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -310,6 +309,44 @@ class ValidatorTest { } } + @Test + public void testInvalidAutomaticModuleName() throws Exception { + System.out.printf("%n%n*****Creating Jar with invalid Automatic-Module-Name in Manifest*****%n%n"); + var file = Path.of("InvalidAutomaticModuleName.jar"); + var manifest = Path.of("MANIFEST.MF"); + Files.writeString(manifest, + """ + Automatic-Module-Name: default + """); + jar("--create --file " + file + " --manifest " + manifest); + var e = assertThrows(IOException.class, () -> jar("--validate --file " + file.toString())); + var err = e.getMessage(); + System.out.println(err); + assertTrue(err.contains("invalid module name of Automatic-Module-Name entry in manifest: default"), "missing warning for: default"); + } + + @Test + public void testWrongAutomaticModuleName() throws Exception { + System.out.printf("%n%n*****Creating Jar with wrong Automatic-Module-Name in Manifest*****%n%n"); + var file = Path.of("WrongAutomaticModuleName.jar"); + var foo = Path.of("module-info.java"); + Files.writeString(foo, + """ + module foo {} + """); + var manifest = Path.of("MANIFEST.MF"); + Files.writeString(manifest, + """ + Automatic-Module-Name: bar + """); + JAVAC_TOOL.run(System.out, System.err, foo.toString()); + jar("--create --file " + file + " --manifest " + manifest + " module-info.class"); + var e = assertThrows(IOException.class, () -> jar("--validate --file " + file.toString())); + var err = e.getMessage(); + System.out.println(err); + assertTrue(err.contains("expected Automatic-Module-Name entry in manifest: bar to match name of compiled module: foo"), "missing warning for: foo/bar"); + } + /** * Validates that base manifest-related entries are at expected LOC positions. *

    diff --git a/test/langtools/ProblemList-StaticJdk.txt b/test/langtools/ProblemList-StaticJdk.txt index 70f0438c0c6..700099656e8 100644 --- a/test/langtools/ProblemList-StaticJdk.txt +++ b/test/langtools/ProblemList-StaticJdk.txt @@ -1,6 +1,6 @@ ########################################################################### # -# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2025, 2026, 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 @@ -24,3 +24,16 @@ ########################################################################### # Currently empty + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/langtools/ProblemList-enable-preview.txt b/test/langtools/ProblemList-enable-preview.txt new file mode 100644 index 00000000000..1a831a0dde3 --- /dev/null +++ b/test/langtools/ProblemList-enable-preview.txt @@ -0,0 +1,32 @@ +# +# Copyright (c) 2025, 2026, 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. +# +# 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. +# + +############################################################################# +# +# List of quarantined tests for testing with --enable-preview +# +# These are failures that ONLY occur with the '--enable-preview' option +# specified. There are separate sub-sections for each preview project. +# +############################################################################# + diff --git a/test/langtools/ProblemList.txt b/test/langtools/ProblemList.txt index 9d61a20663d..07ab6937c07 100644 --- a/test/langtools/ProblemList.txt +++ b/test/langtools/ProblemList.txt @@ -1,6 +1,6 @@ ########################################################################### # -# Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2026, 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 @@ -71,3 +71,16 @@ tools/javap/output/RepeatingTypeAnnotations.java ########################################################################### # # jdeps + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java b/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java index ee1fa538a57..6740bace528 100644 --- a/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java +++ b/test/langtools/jdk/javadoc/doclet/checkStylesheetClasses/CheckStylesheetClasses.java @@ -122,7 +122,7 @@ public class CheckStylesheetClasses { "modifiers", "permits", "return-type"); // misc: these are defined in HtmlStyle, and used by the doclet - removeAll(htmlStyleNames, "col-plain", "external-link", "header", "index", + removeAll(htmlStyleNames, "col-plain", "external-link", "header", "package-uses", "packages", "permits-note", "serialized-package-container", "source-container"); diff --git a/test/langtools/jdk/jshell/SnippetHighlightTest.java b/test/langtools/jdk/jshell/SnippetHighlightTest.java index dbf3418af83..33f7f263617 100644 --- a/test/langtools/jdk/jshell/SnippetHighlightTest.java +++ b/test/langtools/jdk/jshell/SnippetHighlightTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8274148 8301580 8359497 + * @bug 8274148 8301580 8359497 8374293 * @summary Check snippet highlighting * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -122,6 +122,13 @@ public class SnippetHighlightTest extends KullaTesting { """); } + @Test // 8374293: The returned Highlights should not overlap + public void testHighlightsOverlap() { + assertHighlights("public void E test()", "Highlight[start=0, end=6, attributes=[KEYWORD]]", + "Highlight[start=7, end=11, attributes=[KEYWORD]]", + "Highlight[start=14, end=18, attributes=[DECLARATION]]"); + } + private void assertHighlights(String code, String... expected) { List completions = computeHighlights(code); assertEquals(Arrays.asList(expected), completions, "Input: " + code + ", " + completions.toString()); @@ -134,7 +141,6 @@ public class SnippetHighlightTest extends KullaTesting { getAnalysis().highlights(code); return highlights.stream() .map(h -> h.toString()) - .distinct() .collect(Collectors.toList()); } } diff --git a/test/jdk/javax/sql/rowset/serial/SerialClob/SetAsciiStream.java b/test/langtools/tools/javac/lint/AssignShift64Bits.java similarity index 63% rename from test/jdk/javax/sql/rowset/serial/SerialClob/SetAsciiStream.java rename to test/langtools/tools/javac/lint/AssignShift64Bits.java index 10deaf523e9..bb889d09022 100644 --- a/test/jdk/javax/sql/rowset/serial/SerialClob/SetAsciiStream.java +++ b/test/langtools/tools/javac/lint/AssignShift64Bits.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, 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 @@ -19,24 +19,18 @@ * 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. - */ -import javax.sql.rowset.serial.SerialClob; -import javax.sql.rowset.serial.SerialException; - -/** + * * @test - * @bug 7077451 - * @summary tests if the correct exception is thrown when calling method setAsciiStream() on SerialClob + * @bug 8371162 + * @summary Verify no lossy conversion warning for 64 bit shift amount + * @compile -Xlint:lossy-conversions -Werror AssignShift64Bits.java */ -public class SetAsciiStream { - public static void main(String[] args) throws Exception { - SerialClob clob = new SerialClob(new char[0]); - try { - clob.setAsciiStream(0); - } catch (SerialException e) { - System.out.println("Test PASSED"); - } +public class AssignShift64Bits { + void m() { + int a = 1 << 1L; + a <<= 1L; + long b = 1 << 1L; + b <<= 1L; } - } diff --git a/test/langtools/tools/javac/lint/ShiftOutOfRange.out b/test/langtools/tools/javac/lint/ShiftOutOfRange.out index 7aee0b014af..6942e7e6809 100644 --- a/test/langtools/tools/javac/lint/ShiftOutOfRange.out +++ b/test/langtools/tools/javac/lint/ShiftOutOfRange.out @@ -1,17 +1,13 @@ ShiftOutOfRange.java:14:18: compiler.warn.bit.shift.out.of.range: int, -32, 0 ShiftOutOfRange.java:15:18: compiler.warn.bit.shift.out.of.range: int, -32, 0 ShiftOutOfRange.java:16:19: compiler.warn.bit.shift.out.of.range: int, -32, 0 -ShiftOutOfRange.java:17:15: compiler.warn.possible.loss.of.precision: long, int ShiftOutOfRange.java:17:15: compiler.warn.bit.shift.out.of.range: int, -32, 0 ShiftOutOfRange.java:18:15: compiler.warn.bit.shift.out.of.range: int, -32, 0 ShiftOutOfRange.java:19:16: compiler.warn.bit.shift.out.of.range: int, -32, 0 -ShiftOutOfRange.java:25:15: compiler.warn.possible.loss.of.precision: long, int -ShiftOutOfRange.java:32:15: compiler.warn.possible.loss.of.precision: long, int ShiftOutOfRange.java:39:18: compiler.warn.bit.shift.out.of.range: int, 32, 0 ShiftOutOfRange.java:40:18: compiler.warn.bit.shift.out.of.range: int, 32, 0 ShiftOutOfRange.java:41:19: compiler.warn.bit.shift.out.of.range: int, 32, 0 ShiftOutOfRange.java:42:15: compiler.warn.bit.shift.out.of.range: int, 32, 0 -ShiftOutOfRange.java:43:15: compiler.warn.possible.loss.of.precision: long, int ShiftOutOfRange.java:43:15: compiler.warn.bit.shift.out.of.range: int, 32, 0 ShiftOutOfRange.java:44:16: compiler.warn.bit.shift.out.of.range: int, 32, 0 ShiftOutOfRange.java:51:18: compiler.warn.bit.shift.out.of.range: long, -64, 0 @@ -26,4 +22,4 @@ ShiftOutOfRange.java:78:19: compiler.warn.bit.shift.out.of.range: long, 64, 0 ShiftOutOfRange.java:79:15: compiler.warn.bit.shift.out.of.range: long, 64, 0 ShiftOutOfRange.java:80:15: compiler.warn.bit.shift.out.of.range: long, 64, 0 ShiftOutOfRange.java:81:16: compiler.warn.bit.shift.out.of.range: long, 64, 0 -28 warnings +24 warnings diff --git a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java index 4e1a7d7f669..804c2a3f622 100644 --- a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java +++ b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java @@ -1,5 +1,6 @@ /** * @test /nodynamiccopyright/ + * @bug 8375571 * @summary Verify error reports for erroneous deconstruction patterns are sensible * @compile/fail/ref=DeconstructionPatternErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW -XDdev DeconstructionPatternErrors.java */ @@ -39,6 +40,10 @@ public class DeconstructionPatternErrors { boolean b = p instanceof P(int i) p; //introducing a variable for the record pattern } + void typeVarTest(T p) { + if (p instanceof T(int i) && i == 0); //T is a type variable + } + public record P(int i) { } diff --git a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out index f947142cd66..4d21d6069d8 100644 --- a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out +++ b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out @@ -1,23 +1,24 @@ -DeconstructionPatternErrors.java:35:37: compiler.err.illegal.start.of.type -DeconstructionPatternErrors.java:37:28: compiler.err.illegal.start.of.type -DeconstructionPatternErrors.java:39:42: compiler.err.expected: ';' -DeconstructionPatternErrors.java:39:43: compiler.err.not.stmt -DeconstructionPatternErrors.java:15:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List, java.util.ArrayList) -DeconstructionPatternErrors.java:16:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, java.util.ArrayList -DeconstructionPatternErrors.java:17:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int) -DeconstructionPatternErrors.java:18:28: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.String) -DeconstructionPatternErrors.java:19:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, DeconstructionPatternErrors.P) -DeconstructionPatternErrors.java:20:26: compiler.err.incorrect.number.of.nested.patterns: java.lang.Runnable,java.lang.Runnable, java.lang.Runnable +DeconstructionPatternErrors.java:36:37: compiler.err.illegal.start.of.type +DeconstructionPatternErrors.java:38:28: compiler.err.illegal.start.of.type +DeconstructionPatternErrors.java:40:42: compiler.err.expected: ';' +DeconstructionPatternErrors.java:40:43: compiler.err.not.stmt +DeconstructionPatternErrors.java:16:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List, java.util.ArrayList) +DeconstructionPatternErrors.java:17:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, java.util.ArrayList +DeconstructionPatternErrors.java:18:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int) +DeconstructionPatternErrors.java:19:28: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.String) +DeconstructionPatternErrors.java:20:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, DeconstructionPatternErrors.P) DeconstructionPatternErrors.java:21:26: compiler.err.incorrect.number.of.nested.patterns: java.lang.Runnable,java.lang.Runnable, java.lang.Runnable -DeconstructionPatternErrors.java:22:26: compiler.err.incorrect.number.of.nested.patterns: int, int,compiler.misc.type.none -DeconstructionPatternErrors.java:23:26: compiler.err.incorrect.number.of.nested.patterns: int, int,int -DeconstructionPatternErrors.java:24:36: compiler.err.cant.resolve.location: kindname.class, Unresolvable, , , (compiler.misc.location: kindname.class, DeconstructionPatternErrors, null) -DeconstructionPatternErrors.java:24:26: compiler.err.incorrect.number.of.nested.patterns: int, int,Unresolvable -DeconstructionPatternErrors.java:25:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord -DeconstructionPatternErrors.java:26:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord -DeconstructionPatternErrors.java:27:44: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer) -DeconstructionPatternErrors.java:27:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord -DeconstructionPatternErrors.java:28:40: compiler.err.match.binding.exists -DeconstructionPatternErrors.java:29:56: compiler.err.already.defined: kindname.variable, v1, kindname.method, meth() -DeconstructionPatternErrors.java:29:64: compiler.err.already.defined: kindname.variable, v2, kindname.method, meth() -22 errors \ No newline at end of file +DeconstructionPatternErrors.java:22:26: compiler.err.incorrect.number.of.nested.patterns: java.lang.Runnable,java.lang.Runnable, java.lang.Runnable +DeconstructionPatternErrors.java:23:26: compiler.err.incorrect.number.of.nested.patterns: int, int,compiler.misc.type.none +DeconstructionPatternErrors.java:24:26: compiler.err.incorrect.number.of.nested.patterns: int, int,int +DeconstructionPatternErrors.java:25:36: compiler.err.cant.resolve.location: kindname.class, Unresolvable, , , (compiler.misc.location: kindname.class, DeconstructionPatternErrors, null) +DeconstructionPatternErrors.java:25:26: compiler.err.incorrect.number.of.nested.patterns: int, int,Unresolvable +DeconstructionPatternErrors.java:26:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord +DeconstructionPatternErrors.java:27:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord +DeconstructionPatternErrors.java:28:44: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer) +DeconstructionPatternErrors.java:28:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord +DeconstructionPatternErrors.java:29:40: compiler.err.match.binding.exists +DeconstructionPatternErrors.java:30:56: compiler.err.already.defined: kindname.variable, v1, kindname.method, meth() +DeconstructionPatternErrors.java:30:64: compiler.err.already.defined: kindname.variable, v2, kindname.method, meth() +DeconstructionPatternErrors.java:44:26: compiler.err.deconstruction.pattern.only.records: T +23 errors diff --git a/test/lib-test/ProblemList-StaticJdk.txt b/test/lib-test/ProblemList-StaticJdk.txt index 70f0438c0c6..700099656e8 100644 --- a/test/lib-test/ProblemList-StaticJdk.txt +++ b/test/lib-test/ProblemList-StaticJdk.txt @@ -1,6 +1,6 @@ ########################################################################### # -# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2025, 2026, 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 @@ -24,3 +24,16 @@ ########################################################################### # Currently empty + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/lib-test/ProblemList.txt b/test/lib-test/ProblemList.txt index 011466088d3..2a5caedc8d8 100644 --- a/test/lib-test/ProblemList.txt +++ b/test/lib-test/ProblemList.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2020, 2026, 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 @@ -37,3 +37,16 @@ # More than one label is allowed but must be on the same line. # ############################################################################# + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/lib/jdk/test/lib/SA/SATestUtils.java b/test/lib/jdk/test/lib/SA/SATestUtils.java index 01233b5bf55..63f522b3d62 100644 --- a/test/lib/jdk/test/lib/SA/SATestUtils.java +++ b/test/lib/jdk/test/lib/SA/SATestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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,8 +26,15 @@ import jdk.test.lib.JDKToolLauncher; import jdk.test.lib.Platform; import jtreg.SkippedException; +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; @@ -259,4 +266,109 @@ public class SATestUtils { throw new SkippedException("Cannot run this test on OSX if adding privileges is required."); } } + + /** + * Find library file that provides strlen(3), then returns it as libc. + * This method works on Linux only. + * @return path to libc + */ + @SuppressWarnings("restricted") + public static String getLibCPath() { + var linker = Linker.nativeLinker(); + var ptrStrlen = linker.defaultLookup() + .findOrThrow("strlen"); + var strlen = linker.downcallHandle( + ptrStrlen, + FunctionDescriptor.of(linker.canonicalLayouts().get("size_t"), ValueLayout.ADDRESS) + ); + var dladdr = linker.downcallHandle( + linker.defaultLookup().findOrThrow("dladdr"), + FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS) + ); + + var structDLInfo = MemoryLayout.structLayout( + ValueLayout.ADDRESS.withName("dli_fname"), + ValueLayout.ADDRESS.withName("dli_fbase"), + ValueLayout.ADDRESS.withName("dli_sname"), + ValueLayout.ADDRESS.withName("dli_saddr") + ).withName("Dl_info"); + var hndDliFname = structDLInfo.varHandle(MemoryLayout.PathElement.groupElement("dli_fname")); + + try(var arena = Arena.ofConfined()){ + var info = arena.allocate(structDLInfo); + int result = (int)dladdr.invoke(ptrStrlen, info); + if (result == 0) { + throw new RuntimeException("dladdr() returns zero"); + } + + var ptrDliFname = (MemorySegment)hndDliFname.get(info, 0); + var libcPathLen = (long)strlen.invoke(ptrDliFname); + return ptrDliFname.reinterpret(libcPathLen + 1) // +1 for NUL + .getString(0); + } catch (Throwable t) { + throw new RuntimeException("getLibCPath() failed due to Throwable.", t); + } + } + + /** + * Find debuginfo file for the library. + * This method will work on Linux only. + * "readelf" has to be available. + * @return null if debuginfo is not available. + */ + public static String getDebugInfo(String lib) { + try { + // Attempt to find debuginfo in /usr/lib/debug + Path debuginfoPath = Path.of("/usr/lib/debug", lib + ".debug"); + boolean exists = Files.exists(debuginfoPath); + if (!exists) { + // Attempt to find debuginfo with build ID + var proc = (new ProcessBuilder("readelf", "-n", lib)).start(); + try (var reader = proc.inputReader()) { + var buildID = reader.lines() + .filter(l -> l.contains("Build ID:")) + .findAny() + .map(l -> l.replace("Build ID:", "").trim()) + .get(); + String dir = buildID.substring(0, 2); + String file = buildID.substring(2); + debuginfoPath = Path.of("/usr/lib/debug/.build_id", dir, file + ".debug"); + exists = Files.exists(debuginfoPath); + } + } + return exists ? debuginfoPath.toString() : null; + } catch (IOException e) { + throw new RuntimeException("getDebugInfo() failed due to IOException.", e); + } + } + + private static boolean isSymbolAvailableInternal(String lib, String symbol) throws IOException { + var proc = (new ProcessBuilder("nm", lib)).start(); + try (var reader = proc.inputReader()) { + return reader.lines() + .anyMatch(l -> l.endsWith(" " + symbol)); + } + } + + /** + * This method will work on Linux only. + * Both "readelf" and "nm" have to be available. + * @return true if given symbol is available in given lib. + */ + public static boolean isSymbolAvailable(String lib, String symbol) { + try { + // Attempt to find symbol from lib + boolean result = isSymbolAvailableInternal(lib, symbol); + if (!result) { + // Attempt to find symbol from debuginfo + String debuginfoPath = getDebugInfo(lib); + if (debuginfoPath != null) { + result = isSymbolAvailableInternal(debuginfoPath, symbol); + } + } + return result; + } catch (IOException e) { + throw new RuntimeException("isSymbolAvailable() failed due to IOException.", e); + } + } } diff --git a/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java b/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java index 6fa8cb2e408..f66ad542e47 100644 --- a/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java +++ b/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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 @@ -33,7 +33,7 @@ import jtreg.SkippedException; public class OpensslArtifactFetcher { - private static final String OPENSSL_BUNDLE_VERSION = "3.5.1"; + private static final String OPENSSL_BUNDLE_VERSION = "3.5.4"; private static final String OPENSSL_ORG = "jpg.tests.jdk.openssl"; /** diff --git a/test/lib/jdk/test/lib/util/CoreUtils.java b/test/lib/jdk/test/lib/util/CoreUtils.java index f7a486def2b..72c1d89af44 100644 --- a/test/lib/jdk/test/lib/util/CoreUtils.java +++ b/test/lib/jdk/test/lib/util/CoreUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -203,6 +203,7 @@ public class CoreUtils { private static final String CORE_PATTERN_FILE_NAME = "/proc/sys/kernel/core_pattern"; private static final String LOCATION_STRING = "location: "; + private static final String ALT_LOCATION_STRING = "alternatively, falling back to"; private static String parseCoreFileLocationFromOutput(String crashOutputString) { System.out.println("crashOutputString = [" + crashOutputString + "]"); @@ -220,12 +221,25 @@ public class CoreUtils { // Find the core file name in the output. String coreWithPid; - if (stringWithLocation.contains("or ") && !Platform.isWindows()) { - Matcher m = Pattern.compile("or.* ([^ ]+[^\\)])\\)?").matcher(stringWithLocation); + if (Platform.isLinux() && stringWithLocation.contains(ALT_LOCATION_STRING)) { + /* + * If hotspot detects that /proc/sys/kernel/core_pattern starts with a "|", + * it generates a messages something like the following: + * # Core dump will be written. Default location: Determined by the following: + * "/var/bin/core.sh %p" (alternatively, falling back to + * /ws/jdk/build/linux-x64/test-support/jtreg_open_test_hotspot_jtreg_serviceability_sa_ClhsdbFindPC_java/scratch/2/core.10353) + * We try to detect this and glean this alternative path from the message. The + * hope here is that either this is where the core file actually ends up, or that + * the script referenced by the core_pattern is just zipping the core file, in + * which case a call to unzipCores() will have already unzipped the core file + * into this path. + */ + Matcher m = Pattern.compile(ALT_LOCATION_STRING + ".* ([^ ]+[^\\)])\\)?").matcher(stringWithLocation); if (!m.find()) { throw new RuntimeException("Couldn't find path to core inside location string"); } coreWithPid = m.group(1); + System.out.println("getCoreFileLocation found alt coreWithPid = " + coreWithPid); } else { coreWithPid = stringWithLocation.trim(); } diff --git a/test/micro/org/openjdk/bench/java/nio/CharsetCanEncode.java b/test/micro/org/openjdk/bench/java/nio/CharsetCanEncode.java new file mode 100644 index 00000000000..ebfbc217a95 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/nio/CharsetCanEncode.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2026, 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. + * + * 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 org.openjdk.bench.java.nio; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Fork(3) +public class CharsetCanEncode { + + private static final char ALEF_CHAR = '\u05d0'; + private static final String ALEF_STRING = "\u05d0"; + + // sun.nio.cs.US_ASCII + private CharsetEncoder ascii = Charset.forName("US-ASCII").newEncoder(); + + // sun.nio.cs.ISO_8859_1 + private CharsetEncoder iso88591 = Charset.forName("ISO-8859-1").newEncoder(); + + // sun.nio.cs.SingleByte + private CharsetEncoder iso88592 = Charset.forName("ISO-8859-2").newEncoder(); + + // sun.nio.cs.DoubleByte + private CharsetEncoder shiftjis = Charset.forName("Shift_JIS").newEncoder(); + + // sun.nio.cs.UTF_8 + private CharsetEncoder utf8 = Charset.forName("UTF-8").newEncoder(); + + // sun.nio.cs.UTF_16LE + private CharsetEncoder utf16le = Charset.forName("UTF-16LE").newEncoder(); + + @Benchmark + public boolean asciiCanEncodeCharYes() { + return ascii.canEncode('D'); + } + + @Benchmark + public boolean asciiCanEncodeStringYes() { + return ascii.canEncode("D"); + } + + @Benchmark + public boolean asciiCanEncodeCharNo() { + return ascii.canEncode(ALEF_CHAR); + } + + @Benchmark + public boolean asciiCanEncodeStringNo() { + return ascii.canEncode(ALEF_STRING); + } + + @Benchmark + public boolean iso88591CanEncodeCharYes() { + return iso88591.canEncode('D'); + } + + @Benchmark + public boolean iso88591CanEncodeStringYes() { + return iso88591.canEncode("D"); + } + + @Benchmark + public boolean iso88591CanEncodeCharNo() { + return iso88591.canEncode(ALEF_CHAR); + } + + @Benchmark + public boolean iso88591CanEncodeStringNo() { + return iso88591.canEncode(ALEF_STRING); + } + + @Benchmark + public boolean iso88592CanEncodeCharYes() { + return iso88592.canEncode('D'); + } + + @Benchmark + public boolean iso88592CanEncodeStringYes() { + return iso88592.canEncode("D"); + } + + @Benchmark + public boolean iso88592CanEncodeCharNo() { + return iso88592.canEncode(ALEF_CHAR); + } + + @Benchmark + public boolean iso88592CanEncodeStringNo() { + return iso88592.canEncode(ALEF_STRING); + } + + @Benchmark + public boolean shiftjisCanEncodeCharYes() { + return shiftjis.canEncode('D'); + } + + @Benchmark + public boolean shiftjisCanEncodeStringYes() { + return shiftjis.canEncode("D"); + } + + @Benchmark + public boolean shiftjisCanEncodeCharNo() { + return shiftjis.canEncode(ALEF_CHAR); + } + + @Benchmark + public boolean shiftjisCanEncodeStringNo() { + return shiftjis.canEncode(ALEF_STRING); + } + + @Benchmark + public boolean utf8CanEncodeCharYes() { + return utf8.canEncode('D'); + } + + @Benchmark + public boolean utf8CanEncodeStringYes() { + return utf8.canEncode("D"); + } + + @Benchmark + public boolean utf8CanEncodeCharNo() { + return utf8.canEncode(Character.MIN_SURROGATE); + } + + @Benchmark + public boolean utf8CanEncodeStringNo() { + return utf8.canEncode(String.valueOf(Character.MIN_SURROGATE)); + } + + @Benchmark + public boolean utf16leCanEncodeCharYes() { + return utf16le.canEncode('D'); + } + + @Benchmark + public boolean utf16leCanEncodeStringYes() { + return utf16le.canEncode("D"); + } + + @Benchmark + public boolean utf16leCanEncodeCharNo() { + return utf16le.canEncode(Character.MIN_SURROGATE); + } + + @Benchmark + public boolean utf16leCanEncodeStringNo() { + return utf16le.canEncode(String.valueOf(Character.MIN_SURROGATE)); + } +} diff --git a/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithms.java b/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithms.java new file mode 100644 index 00000000000..911494cb022 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithms.java @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2026, 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. + * + * 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 org.openjdk.bench.vm.compiler; + +import java.util.Arrays; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.*; + +/** + * The goal of this benchmark is to show the power of auto vectorization + * and the Vector API. + * + * Please only modify this benchark in synchronization with the IR test: + * test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java + * + * You may want to play with the following VM flags: + * - Disable auto vectorization: + * -XX:+UnlockDiagnosticVMOptions -XX:AutoVectorizationOverrideProfitability=0 + * - Smaller vector size: + * -XX:MaxVectorSize=16 + * - Disable fill loop detection, so we don't use intrinsic but auto vectorization: + * -XX:-OptimizeFill + * - Lilliput can also have an effect, because it can change alignment and have + * an impact on which exact intrinsic is chosen (e.g. fill and copy): + * -XX:+UseCompactObjectHeaders + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 3, time = 1) +@Fork(value = 5, jvmArgs = {"--add-modules=jdk.incubator.vector", "-XX:CompileCommand=inline,*VectorAlgorithmsImpl*::*"}) +public class VectorAlgorithms { + @Param({"640000"}) + public int SIZE; + + @Param({"10000"}) + public int NUM_X_OBJECTS; + + @Param({"0"}) + public int SEED; + + VectorAlgorithmsImpl.Data d; + + @Setup + public void init() { + d = new VectorAlgorithmsImpl.Data(SIZE, SEED, NUM_X_OBJECTS); + } + + // ------------------------------------------------------------------------------------------ + // Benchmarks just forward arguments and returns. + // ------------------------------------------------------------------------------------------ + + @Benchmark + public Object fillI_loop() { + return VectorAlgorithmsImpl.fillI_loop(d.rI1); + } + + @Benchmark + public Object fillI_VectorAPI() { + return VectorAlgorithmsImpl.fillI_VectorAPI(d.rI1); + } + + @Benchmark + public Object fillI_Arrays() { + return VectorAlgorithmsImpl.fillI_Arrays(d.rI1); + } + + @Benchmark + public Object iotaI_loop() { + return VectorAlgorithmsImpl.iotaI_loop(d.rI1); + } + + @Benchmark + public Object iotaI_VectorAPI() { + return VectorAlgorithmsImpl.iotaI_VectorAPI(d.rI1); + } + + @Benchmark + public Object copyI_loop() { + return VectorAlgorithmsImpl.copyI_loop(d.aI, d.rI1); + } + + @Benchmark + public Object copyI_VectorAPI() { + return VectorAlgorithmsImpl.copyI_VectorAPI(d.aI, d.rI1); + } + + @Benchmark + public Object copyI_System_arraycopy() { + return VectorAlgorithmsImpl.copyI_System_arraycopy(d.aI, d.rI1); + } + + @Benchmark + public Object mapI_loop() { + return VectorAlgorithmsImpl.mapI_loop(d.aI, d.rI1); + } + + @Benchmark + public Object mapI_VectorAPI() { + return VectorAlgorithmsImpl.mapI_VectorAPI(d.aI, d.rI1); + } + + @Benchmark + public int reduceAddI_loop() { + return VectorAlgorithmsImpl.reduceAddI_loop(d.aI); + } + + @Benchmark + public int reduceAddI_reassociate() { + return VectorAlgorithmsImpl.reduceAddI_reassociate(d.aI); + } + + @Benchmark + public int reduceAddI_VectorAPI_naive() { + return VectorAlgorithmsImpl.reduceAddI_VectorAPI_naive(d.aI); + } + + @Benchmark + public int reduceAddI_VectorAPI_reduction_after_loop() { + return VectorAlgorithmsImpl.reduceAddI_VectorAPI_reduction_after_loop(d.aI); + } + + @Benchmark + public float dotProductF_loop() { + return VectorAlgorithmsImpl.dotProductF_loop(d.aF, d.bF); + } + + @Benchmark + public float dotProductF_VectorAPI_naive() { + return VectorAlgorithmsImpl.dotProductF_VectorAPI_naive(d.aF, d.bF); + } + + @Benchmark + public float dotProductF_VectorAPI_reduction_after_loop() { + return VectorAlgorithmsImpl.dotProductF_VectorAPI_reduction_after_loop(d.aF, d.bF); + } + + @Benchmark + public int hashCodeB_loop() { + return VectorAlgorithmsImpl.hashCodeB_loop(d.aB); + } + + @Benchmark + public int hashCodeB_Arrays() { + return VectorAlgorithmsImpl.hashCodeB_Arrays(d.aB); + } + + @Benchmark + public int hashCodeB_VectorAPI_v1() { + return VectorAlgorithmsImpl.hashCodeB_VectorAPI_v1(d.aB); + } + + @Benchmark + public int hashCodeB_VectorAPI_v2() { + return VectorAlgorithmsImpl.hashCodeB_VectorAPI_v2(d.aB); + } + + @Benchmark + public Object scanAddI_loop() { + return VectorAlgorithmsImpl.scanAddI_loop(d.aI, d.rI1); + } + + @Benchmark + public Object scanAddI_loop_reassociate() { + return VectorAlgorithmsImpl.scanAddI_loop_reassociate(d.aI, d.rI1); + } + + @Benchmark + public Object scanAddI_VectorAPI_permute_add() { + return VectorAlgorithmsImpl.scanAddI_VectorAPI_permute_add(d.aI, d.rI1); + } + + @Benchmark + public int findMinIndexI_loop() { + return VectorAlgorithmsImpl.findMinIndexI_loop(d.aI); + } + + @Benchmark + public int findMinIndexI_VectorAPI() { + return VectorAlgorithmsImpl.findMinIndexI_VectorAPI(d.aI); + } + + @Benchmark + public int findI_loop() { + // Every invocation should have a different value for e, so that + // we don't get branch-prediction that is too good. And also so + // that the position where we exit is more evenly distributed. + d.eI_idx = (d.eI_idx + 1) & 0xffff; + int e = d.eI[d.eI_idx]; + return VectorAlgorithmsImpl.findI_loop(d.aI, e); + } + + @Benchmark + public int findI_VectorAPI() { + d.eI_idx = (d.eI_idx + 1) & 0xffff; + int e = d.eI[d.eI_idx]; + return VectorAlgorithmsImpl.findI_VectorAPI(d.aI, e); + } + + @Benchmark + public Object reverseI_loop() { + return VectorAlgorithmsImpl.reverseI_loop(d.aI, d.rI1); + } + + @Benchmark + public Object reverseI_VectorAPI() { + return VectorAlgorithmsImpl.reverseI_VectorAPI(d.aI, d.rI1); + } + + @Benchmark + public Object filterI_loop() { + // Every invocation should have a different value for e, so that + // we don't get branch-prediction that is too good. And also so + // That the length of the resulting data is more evenly distributed. + d.eI_idx = (d.eI_idx + 1) & 0xffff; + int e = d.eI[d.eI_idx]; + return VectorAlgorithmsImpl.filterI_loop(d.aI, d.rI1, e); + } + + @Benchmark + public Object filterI_VectorAPI() { + d.eI_idx = (d.eI_idx + 1) & 0xffff; + int e = d.eI[d.eI_idx]; + return VectorAlgorithmsImpl.filterI_VectorAPI(d.aI, d.rI1, e); + } + + @Benchmark + public int reduceAddIFieldsX4_loop() { + return VectorAlgorithmsImpl.reduceAddIFieldsX4_loop(d.oopsX4, d.memX4); + } + + @Benchmark + public int reduceAddIFieldsX4_VectorAPI() { + return VectorAlgorithmsImpl.reduceAddIFieldsX4_VectorAPI(d.oopsX4, d.memX4); + } + + @Benchmark + public Object lowerCaseB_loop() { + return VectorAlgorithmsImpl.lowerCaseB_loop(d.strB, d.rB1); + } + + @Benchmark + public Object lowerCaseB_VectorAPI_v1() { + return VectorAlgorithmsImpl.lowerCaseB_VectorAPI_v1(d.strB, d.rB1); + } + + @Benchmark + public Object lowerCaseB_VectorAPI_v2() { + return VectorAlgorithmsImpl.lowerCaseB_VectorAPI_v2(d.strB, d.rB1); + } +} diff --git a/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java b/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java new file mode 100644 index 00000000000..5ab057329d3 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java @@ -0,0 +1,775 @@ +/* + * Copyright (c) 2026, 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. + * + * 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 org.openjdk.bench.vm.compiler; + +import java.util.Arrays; +import java.util.Random; +import jdk.incubator.vector.*; + +/** + * The code below is supposed to be an exact copy of: + * test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java + */ +public class VectorAlgorithmsImpl { + private static final VectorSpecies SPECIES_I = IntVector.SPECIES_PREFERRED; + private static final VectorSpecies SPECIES_I512 = IntVector.SPECIES_512; + private static final VectorSpecies SPECIES_I256 = IntVector.SPECIES_256; + private static final VectorSpecies SPECIES_B = ByteVector.SPECIES_PREFERRED; + private static final VectorSpecies SPECIES_B64 = ByteVector.SPECIES_64; + private static final VectorSpecies SPECIES_F = FloatVector.SPECIES_PREFERRED; + + // This class stores the input and output arrays. + // The constructor sets up all the data. + // + // IMPORTANT: + // If you want to use some array but do NOT modify it: just use it. + // If you want to use it and DO want to modify it: clone it. This + // ensures that each test gets a separate copy, and that when we + // capture the modified arrays they are different for every method + // and run. + // An alternative to cloning is to use different return arrays for + // different implementations of the same group, e.g. rI1, rI2, ... + // + public static class Data { + public int[] aI; + public int[] rI1; + public int[] rI2; + public int[] rI3; + public int[] rI4; + public int[] eI; + // The test has to use the same index into eI for all implementations. But in the + // benchmark, we'd like to use random indices, so we use the index to advance through + // the array. + public int eI_idx = 0; + + public float[] aF; + public float[] bF; + + public byte[] aB; + public byte[] strB; + public byte[] rB1; + public byte[] rB2; + public byte[] rB3; + + public int[] oopsX4; + public int[] memX4; + + public Data(int size, int seed, int numX4Objects) { + Random random = new Random(seed); + + // int: one input array and multiple output arrays so different implementations can + // store their results to different arrays. + aI = new int[size]; + rI1 = new int[size]; + rI2 = new int[size]; + rI3 = new int[size]; + rI4 = new int[size]; + Arrays.setAll(aI, i -> random.nextInt()); + + // Populate with some random values from aI, and some totally random values. + eI = new int[0x10000]; + for (int i = 0; i < eI.length; i++) { + eI[i] = (random.nextInt(10) == 0) ? random.nextInt() : aI[random.nextInt(size)]; + } + + // X4 oop setup. + // oopsX4 holds "addresses" (i.e. indices), that point to the 16-byte objects in memX4. + oopsX4 = new int[size]; + memX4 = new int[numX4Objects * 4]; + for (int i = 0; i < size; i++) { + // assign either a zero=null, or assign a random oop. + oopsX4[i] = (random.nextInt(10) == 0) ? 0 : random.nextInt(numX4Objects) * 4; + } + // Just fill the whole array with random values. + // The relevant field is only at every "4 * i + 3" though. + memX4 = new int[4 * numX4Objects]; + for (int i = 0; i < memX4.length; i++) { + memX4[i] = random.nextInt(); + } + + // float inputs. To avoid rounding issues, only use small integers. + aF = new float[size]; + bF = new float[size]; + for (int i = 0; i < size; i++) { + aF[i] = random.nextInt(32) - 16; + bF[i] = random.nextInt(32) - 16; + } + + // byte: just random data. + aB = new byte[size]; + strB = new byte[size]; + rB1 = new byte[size]; + rB2 = new byte[size]; + rB3 = new byte[size]; + random.nextBytes(aB); + random.nextBytes(strB); // TODO: special data! + } + } + + public static Object fillI_loop(int[] r) { + for (int i = 0; i < r.length; i++) { + r[i] = 42; + } + return r; + } + + public static Object fillI_Arrays(int[] r) { + Arrays.fill(r, 42); + return r; + } + + public static Object fillI_VectorAPI(int[] r) { + var v = IntVector.broadcast(SPECIES_I, 42); + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + v.intoArray(r, i); + } + for (; i < r.length; i++) { + r[i] = 42; + } + return r; + } + + public static Object iotaI_loop(int[] r) { + for (int i = 0; i < r.length; i++) { + r[i] = i; + } + return r; + } + + public static Object iotaI_VectorAPI(int[] r) { + var iota = IntVector.broadcast(SPECIES_I, 0).addIndex(1); + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + iota.intoArray(r, i); + iota = iota.add(SPECIES_I.length()); + } + for (; i < r.length; i++) { + r[i] = i; + } + return r; + } + + public static Object copyI_loop(int[] a, int[] r) { + for (int i = 0; i < a.length; i++) { + r[i] = a[i]; + } + return r; + } + + public static Object copyI_System_arraycopy(int[] a, int[] r) { + System.arraycopy(a, 0, r, 0, a.length); + return r; + } + + public static Object copyI_VectorAPI(int[] a, int[] r) { + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + v.intoArray(r, i); + } + for (; i < r.length; i++) { + r[i] = a[i]; + } + return r; + } + + public static Object mapI_loop(int[] a, int[] r) { + for (int i = 0; i < a.length; i++) { + r[i] = a[i] * 42; + } + return r; + } + + public static Object mapI_VectorAPI(int[] a, int[] r) { + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + v = v.mul(42); + v.intoArray(r, i); + } + for (; i < r.length; i++) { + r[i] = a[i] * 42; + } + return r; + } + + public static int reduceAddI_loop(int[] a) { + int sum = 0; + for (int i = 0; i < a.length; i++) { + // Relying on simple reduction loop should vectorize since JDK26. + sum += a[i]; + } + return sum; + } + + public static int reduceAddI_reassociate(int[] a) { + int sum = 0; + int i; + for (i = 0; i < a.length - 3; i += 4) { + // Unroll 4x, reassociate inside. + sum += a[i] + a[i + 1] + a[i + 2] + a[i + 3]; + } + for (; i < a.length; i++) { + // Tail + sum += a[i]; + } + return sum; + } + + public static int reduceAddI_VectorAPI_naive(int[] a) { + var sum = 0; + int i; + for (i = 0; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + // reduceLanes in loop is better than scalar performance, but still + // relatively slow. + sum += v.reduceLanes(VectorOperators.ADD); + } + for (; i < a.length; i++) { + sum += a[i]; + } + return sum; + } + + public static int reduceAddI_VectorAPI_reduction_after_loop(int[] a) { + var acc = IntVector.broadcast(SPECIES_I, 0); + int i; + for (i = 0; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + // Element-wide addition into a vector of partial sums is much faster. + // Now, we only need to do a reduceLanes after the loop. + // This works because int-addition is associative and commutative. + acc = acc.add(v); + } + int sum = acc.reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + sum += a[i]; + } + return sum; + } + + public static float dotProductF_loop(float[] a, float[] b) { + float sum = 0; + for (int i = 0; i < a.length; i++) { + sum += a[i] * b[i]; + } + return sum; + } + + public static float dotProductF_VectorAPI_naive(float[] a, float[] b) { + float sum = 0; + int i; + for (i = 0; i < SPECIES_F.loopBound(a.length); i += SPECIES_F.length()) { + var va = FloatVector.fromArray(SPECIES_F, a, i); + var vb = FloatVector.fromArray(SPECIES_F, b, i); + sum += va.mul(vb).reduceLanes(VectorOperators.ADD); + } + for (; i < a.length; i++) { + sum += a[i] * b[i]; + } + return sum; + } + + public static float dotProductF_VectorAPI_reduction_after_loop(float[] a, float[] b) { + var sums = FloatVector.broadcast(SPECIES_F, 0.0f); + int i; + for (i = 0; i < SPECIES_F.loopBound(a.length); i += SPECIES_F.length()) { + var va = FloatVector.fromArray(SPECIES_F, a, i); + var vb = FloatVector.fromArray(SPECIES_F, b, i); + sums = sums.add(va.mul(vb)); + } + float sum = sums.reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + sum += a[i] * b[i]; + } + return sum; + } + + public static int hashCodeB_loop(byte[] a) { + int h = 1; + for (int i = 0; i < a.length; i++) { + h = 31 * h + a[i]; + } + return h; + } + + public static int hashCodeB_Arrays(byte[] a) { + return Arrays.hashCode(a); + } + + // Simplified intrinsic code from C2_MacroAssembler::arrays_hashcode in c2_MacroAssembler_x86.cpp + // + // Ideas that may help understand the code: + // h(i) = 31 * h(i-1) + a[i] + // "unroll" by factor of L=8: + // h(i+8) = h(i) * 31^8 + a[i+1] * 31^7 + a[i+2] * 31^6 + ... + a[i+8] * 1 + // ----------- ------------------------------------------------ + // scalar vector: notice the powers of 31 in reverse + // + // We notice that we can load a[i+1 .. i+8], then element-wise multiply with + // the vector of reversed powers-of-31, and then do reduceLanes(ADD). + // But we can do even better: By looking at multiple such 8-unrolled iterations. + // Instead of applying the "next" factor of "31^8" to the reduced scalar, we can + // already apply it element-wise. That allows us to move the reduction out + // of the loop. + // + // Note: the intrinsic additionally unrolls the loop by a factor of 4, + // but we want to keep thins simple for demonstration purposes. + // + private static int[] REVERSE_POWERS_OF_31 = new int[9]; + static { + int p = 1; + for (int i = REVERSE_POWERS_OF_31.length - 1; i >= 0; i--) { + REVERSE_POWERS_OF_31[i] = p; + p *= 31; + } + } + public static int hashCodeB_VectorAPI_v1(byte[] a) { + int result = 1; // initialValue + var vresult = IntVector.zero(SPECIES_I256); + int next = REVERSE_POWERS_OF_31[0]; // 31^L + var vcoef = IntVector.fromArray(SPECIES_I256, REVERSE_POWERS_OF_31, 1); // powers of 2 in reverse + int i; + for (i = 0; i < SPECIES_B64.loopBound(a.length); i += SPECIES_B64.length()) { + // scalar part: result *= 31^L + result *= next; + // vector part: element-wise apply the next factor and add in the new values. + var vb = ByteVector.fromArray(SPECIES_B64, a, i); + var vi = vb.castShape(SPECIES_I256, 0); + vresult = vresult.mul(next).add(vi); + } + // reduce the partial hashes in the elements, using the reverse list of powers of 2. + result += vresult.mul(vcoef).reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + result = 31 * result + a[i]; + } + return result; + } + + // This second approach follows the idea from this blog post by Otmar Ertl: + // https://www.dynatrace.com/news/blog/java-arrays-hashcode-byte-efficiency-techniques/ + // + // I simplified the algorithm a little, so that it is a bit closer + // to the solution "v1" above. + // + // The major issue with "v1" is that we cannot load a full vector of bytes, + // because of the cast to ints. So we can only fill 1/4 of the maximal + // vector size. The trick here is to do an unrolling of factor 4, from: + // h(i) = 31 * h(i-1) + a[i] + // to: + // h(i+4) = h(i) * 31^4 + a[i + 1] * 31^3 + // + a[i + 2] * 31^2 + // + a[i + 3] * 31^1 + // + a[i + 4] * 31^0 + // The goal is now to compute this value for 4 bytes within a 4 byte + // lane of the vector. One concern is that we start with byte values, + // but need to do int-multiplication with powers of 31. If we instead + // did a byte-multiplication, we could get overflows that we would not + // have had in the int-multiplication. + // One trick that helps with chaning the size of the lanes from byte + // to short to int is doing all operations with unsigned integers. That + // way, we can zero-extend instead of sign-bit extend. The first step + // is thus to convert the bytes into unsigned values. Since byte is in + // range [-128..128), doing "a[i+j] + 128" makes it a positive value, + // allowing for unsigned multiplication. + // h(i+4) = h(i) * 31^4 + a[i + 1] * 31^3 + // + a[i + 2] * 31^2 + // + a[i + 3] * 31^1 + // + a[i + 4] * 31^0 + // = h(i) * 31^4 + (a[i + 1] + 128 - 128) * 31^3 + // + (a[i + 2] + 128 - 128) * 31^2 + // + (a[i + 3] + 128 - 128) * 31^1 + // + (a[i + 4] + 128 - 128) * 31^0 + // = h(i) * 31^4 + (a[i + 1] + 128 ) * 31^3 + // + (a[i + 2] + 128 ) * 31^2 + // + (a[i + 3] + 128 ) * 31^1 + // + (a[i + 4] + 128 ) * 31^0 + // + -128 * (31^3 + 31^2 + 31^1 + 1) + // = h(i) * 31^4 + ((a[i + 1] + 128) * 31 + // + (a[i + 2] + 128 ) * 31^2 + // + ((a[i + 3] + 128) * 31 + // + (a[i + 4] + 128 ) + // + -128 * (31^3 + 31^2 + 31^1 + 1) + // + // Getting from the signed a[i] value to unsigned with +128, we can + // just xor with 0x80=128. Any numbers there in range [-128..0) are + // now in range [0..128). And any numbers that were in range [0..128) + // are now in unsigned range [128..255). What a neat trick! + // + // We then apply a byte->short transition where we crunch 2 bytes + // into one short, applying a multiplication with 31 to one of the + // two bytes. This multiplication cannot overflow in a short. + // then we apply a short->int transition where we crunch 2 shorts + // into one int, applying a multiplication with 31^2 to one of the + // two shorts. This multiplication cannot overflow in an int. + // + public static int hashCodeB_VectorAPI_v2(byte[] a) { + return HashCodeB_VectorAPI_V2.compute(a); + } + + private static class HashCodeB_VectorAPI_V2 { + private static final int L = Math.min(ByteVector.SPECIES_PREFERRED.length(), + IntVector.SPECIES_PREFERRED.length() * 4); + private static final VectorShape SHAPE = VectorShape.forBitSize(8 * L); + private static final VectorSpecies SPECIES_B = SHAPE.withLanes(byte.class); + private static final VectorSpecies SPECIES_I = SHAPE.withLanes(int.class); + + private static int[] REVERSE_POWERS_OF_31_STEP_4 = new int[L / 4 + 1]; + static { + int p = 1; + int step = 31 * 31 * 31 * 31; // step by 4 + for (int i = REVERSE_POWERS_OF_31_STEP_4.length - 1; i >= 0; i--) { + REVERSE_POWERS_OF_31_STEP_4[i] = p; + p *= step; + } + } + + public static int compute(byte[] a) { + int result = 1; // initialValue + int next = REVERSE_POWERS_OF_31_STEP_4[0]; // 31^L + var vcoef = IntVector.fromArray(SPECIES_I, REVERSE_POWERS_OF_31_STEP_4, 1); // W + var vresult = IntVector.zero(SPECIES_I); + int i; + for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { + var vb = ByteVector.fromArray(SPECIES_B, a, i); + // Add 128 to each byte. + var vs = vb.lanewise(VectorOperators.XOR, (byte)0x80) + .reinterpretAsShorts(); + // Each short lane contains 2 bytes, crunch them. + var vi = vs.and((short)0xff) // lower byte + .mul((short)31) + .add(vs.lanewise(VectorOperators.LSHR, 8)) // upper byte + .reinterpretAsInts(); + // Each int contains 2 shorts, crunch them. + var v = vi.and(0xffff) // lower short + .mul(31 * 31) + .add(vi.lanewise(VectorOperators.LSHR, 16)); // upper short + // Add the correction for the 128 additions above. + v = v.add(-128 * (31*31*31 + 31*31 + 31 + 1)); + // Every element of v now contains a crunched int-package of 4 bytes. + result *= next; + vresult = vresult.mul(next).add(v); + } + result += vresult.mul(vcoef).reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + result = 31 * result + a[i]; + } + return result; + } + } + + public static Object scanAddI_loop(int[] a, int[] r) { + int sum = 0; + for (int i = 0; i < a.length; i++) { + sum += a[i]; + r[i] = sum; + } + return r; + } + + public static Object scanAddI_loop_reassociate(int[] a, int[] r) { + int sum = 0; + int i = 0; + for (; i < a.length - 3; i += 4) { + // We cut the latency by a factor of 4, but increase the number of additions. + int old_sum = sum; + int v0 = a[i + 0]; + int v1 = a[i + 1]; + int v2 = a[i + 2]; + int v3 = a[i + 3]; + int v01 = v0 + v1; + int v23 = v2 + v3; + int v0123 = v01 + v23; + sum += v0123; + r[i + 0] = old_sum + v0; + r[i + 1] = old_sum + v01; + r[i + 2] = old_sum + v01 + v2; + r[i + 3] = old_sum + v0123; + } + for (; i < a.length; i++) { + sum += a[i]; + r[i] = sum; + } + return r; + } + + public static Object scanAddI_VectorAPI_permute_add(int[] a, int[] r) { + // Using Naive Parallel Algorithm: Hills and Steele + int sum = 0; + int xx = 0; // masked later anyway + var shf1 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, 0); + var shf2 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 0); + var shf3 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, xx, xx, xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 0); + var shf4 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, xx, xx, xx, xx, xx, xx, xx, 0, 1, 2, 3, 4, 5, 6, 7}, 0); + var mask1 = VectorMask.fromLong(SPECIES_I512, 0b1111111111111110); + var mask2 = VectorMask.fromLong(SPECIES_I512, 0b1111111111111100); + var mask3 = VectorMask.fromLong(SPECIES_I512, 0b1111111111110000); + var mask4 = VectorMask.fromLong(SPECIES_I512, 0b1111111100000000); + int i = 0; + for (; i < SPECIES_I512.loopBound(a.length); i += SPECIES_I512.length()) { + IntVector v = IntVector.fromArray(SPECIES_I512, a, i); + v = v.add(v.rearrange(shf1), mask1); + v = v.add(v.rearrange(shf2), mask2); + v = v.add(v.rearrange(shf3), mask3); + v = v.add(v.rearrange(shf4), mask4); + v = v.add(sum); + v.intoArray(r, i); + sum = v.lane(SPECIES_I512.length() - 1); + } + for (; i < a.length; i++) { + sum += a[i]; + r[i] = sum; + } + return r; + } + + public static int findMinIndexI_loop(int[] a) { + int min = a[0]; + int index = 0; + for (int i = 1; i < a.length; i++) { + int ai = a[i]; + if (ai < min) { + min = ai; + index = i; + } + } + return index; + } + + public static int findMinIndexI_VectorAPI(int[] a) { + // Main approach: have partial results in mins and idxs. + var mins = IntVector.broadcast(SPECIES_I, a[0]); + var idxs = IntVector.broadcast(SPECIES_I, 0); + var iota = IntVector.broadcast(SPECIES_I, 0).addIndex(1); + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + var mask = v.compare(VectorOperators.LT, mins); + mins = mins.blend(v, mask); + idxs = idxs.blend(iota, mask); + iota = iota.add(SPECIES_I.length()); + } + // Reduce the vectors down + int min = mins.reduceLanes(VectorOperators.MIN); + var not_min_mask = mins.compare(VectorOperators.NE, min); + int index = idxs.blend(a.length, not_min_mask).reduceLanes(VectorOperators.MIN); + // Tail loop + for (; i < a.length; i++) { + int ai = a[i]; + if (ai < min) { + min = ai; + index = i; + } + } + return index; + } + + public static int findI_loop(int[] a, int e) { + for (int i = 0; i < a.length; i++) { + int ai = a[i]; + if (ai == e) { + return i; + } + } + return -1; + } + + public static int findI_VectorAPI(int[] a, int e) { + var es = IntVector.broadcast(SPECIES_I, e); + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + var mask = v.compare(VectorOperators.EQ, es); + if (mask.anyTrue()) { + return i + mask.firstTrue(); + } + } + for (; i < a.length; i++) { + int ai = a[i]; + if (ai == e) { + return i; + } + } + return -1; + } + + public static Object reverseI_loop(int[] a, int[] r) { + for (int i = 0; i < a.length; i++) { + r[a.length - i - 1] = a[i]; + } + return r; + } + + private static final VectorShuffle REVERSE_SHUFFLE_I = SPECIES_I.iotaShuffle(SPECIES_I.length()-1, -1, true); + + public static Object reverseI_VectorAPI(int[] a, int[] r) { + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + v = v.rearrange(REVERSE_SHUFFLE_I); + v.intoArray(r, r.length - SPECIES_I.length() - i); + } + for (; i < a.length; i++) { + r[a.length - i - 1] = a[i]; + } + return r; + } + + public static Object filterI_loop(int[] a, int[] r, int threshold) { + int j = 0; + for (int i = 0; i < a.length; i++) { + int ai = a[i]; + if (ai >= threshold) { + r[j++] = ai; + } + } + // Just force the resulting length onto the same array. + r[r.length - 1] = j; + return r; + } + + public static Object filterI_VectorAPI(int[] a, int[] r, int threshold) { + var thresholds = IntVector.broadcast(SPECIES_I, threshold); + int j = 0; + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + var mask = v.compare(VectorOperators.GE, thresholds); + v = v.compress(mask); + int trueCount = mask.trueCount(); + var prefixMask = mask.compress(); + v.intoArray(r, j, prefixMask); + j += trueCount; + } + + for (; i < a.length; i++) { + int ai = a[i]; + if (ai >= threshold) { + r[j++] = ai; + } + } + // Just force the resulting length onto the same array. + r[r.length - 1] = j; + return r; + } + + // X4: ints simulate 4-byte oops. + // oops: if non-zero (= non-null), every entry simulates a 4-byte oop, pointing into mem. + // mem: an int array that simulates the memory. + // + // Task: Find all non-null oops, and dereference them, get the relevant field. + // Objects have 16 bytes, and the relevant field is at bytes 12-16. + // That maps to 4 ints, and the relevant field is the 4th element of 4. + // Sum up all the field values. + public static int reduceAddIFieldsX4_loop(int[] oops, int[] mem) { + int sum = 0; + for (int i = 0; i < oops.length; i++) { + int oop = oops[i]; + if (oop != 0) { + int fieldValue = mem[oop + 3]; // oop+12 + sum += fieldValue; + } + } + return sum; + } + + public static int reduceAddIFieldsX4_VectorAPI(int[] oops, int[] mem) { + var acc = IntVector.broadcast(SPECIES_I, 0); + int i = 0; + for (; i < SPECIES_I.loopBound(oops.length); i += SPECIES_I.length()) { + var oopv = IntVector.fromArray(SPECIES_I, oops, i); + var mask = oopv.compare(VectorOperators.NE, /* null */0); + // We are lucky today: we need to access mem[oop + 3] + var fieldValues = IntVector.fromArray(SPECIES_I, mem, 3, oops, i, mask); + acc = acc.add(fieldValues); + } + int sum = acc.reduceLanes(VectorOperators.ADD); + for (; i < oops.length; i++) { + int oop = oops[i]; + if (oop != 0) { + int fieldValue = mem[oop + 3]; // oop+12 + sum += fieldValue; + } + } + return sum; + } + + // The lowerCase example demonstrates a lane-wise control-flow diamond. + public static Object lowerCaseB_loop(byte[] a, byte[] r) { + for (int i = 0; i < a.length; i++) { + byte c = a[i]; + if (c >= 'A' && c <= 'Z') { + c += ('a' - 'A'); // c += 32 + } + r[i] = c; + } + return r; + } + + // Control-flow diamonds can easily be simulated by "if-conversion", i.e. + // by using masked operations. An alternative would be to use blend. + public static Object lowerCaseB_VectorAPI_v1(byte[] a, byte[] r) { + int i; + for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { + var vc = ByteVector.fromArray(SPECIES_B, a, i); + var maskA = vc.compare(VectorOperators.GE, (byte)'A'); + var maskZ = vc.compare(VectorOperators.LE, (byte)'Z'); + var mask = maskA.and(maskZ); + vc = vc.add((byte)32, mask); + vc.intoArray(r, i); + } + for (; i < a.length; i++) { + byte c = a[i]; + if (c >= 'A' && c <= 'Z') { + c += ('a' - 'A'); + } + r[i] = c; + } + return r; + } + + public static Object lowerCaseB_VectorAPI_v2(byte[] a, byte[] r) { + int i; + for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { + var vc = ByteVector.fromArray(SPECIES_B, a, i); + // We can convert the range 65..90 (represents ascii A..Z) into a range 0..25. + // This allows us to only use a single unsigned comparison. + var vt = vc.add((byte)-'A'); + var mask = vt.compare(VectorOperators.ULE, (byte)25); + vc = vc.add((byte)32, mask); + vc.intoArray(r, i); + } + for (; i < a.length; i++) { + byte c = a[i]; + if (c >= 'A' && c <= 'Z') { + c += ('a' - 'A'); + } + r[i] = c; + } + return r; + } +} +