/
githubmirror
/
riscv-port
Обзор
Документация
Войти
/
githubmirror
/
riscv-port
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/java.base/share/classes/jdk/internal/lang/CaseFolding.java.template
214 строк
9 KB
Naoto Sato
8378992: Case folding cache should not look up code point U+0000
04 мар 2026, 20:16
04 мар 2026, 20:16
4d2c537
Код
Авторство
О чём код?
/* * 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. 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.lang; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.IntStream; import static java.util.Map.entry; /** * Utility class that handles Unicode case folding properties defined in * CasingFolding.txt, including 1:M full case folding. */ public final class CaseFolding { private CaseFolding() {} /** * Tests whether the specified code point has a folding mapping entry defined. * * @param cp * the Unicode code point to test * @return {@code true} if the given code point has a case folding mapping entry * defined in (@code caseFoldingMap}, {@code false} otherwise */ public static boolean isDefined(int cp) { return getDefined(cp) != -1; } /** * Returns the case-folded form of the specified code point according * to the Unicode case folding mappings. * <p> * If the code point has no case folding mapping defined, this method returns * the original code point. * * Possible combinations of the returning case-folding form as a long value * * +---+---------+--------+---------+--------+--------+ * | 1:1 mapping | 0000 | 0000 | 000x | xxxx | 0041 => 0061 or 1E921 => 1E943 * +---+---------+--------+---------+--------+--------+ * | 1:2 mapping | 0002 | 0000 | xxxx | xxxx | FB02 => 0066 006C * +---+---------+--------+---------+--------+--------+ * | 1:3 mapping | 0003 | xxxx | xxxx | xxxx | FB03 => 0066 0066 0069 * +---+---------+--------+---------+--------+--------+ * * @param cp * the Unicode code point to fold * @return a long value representing the case-folded form of the input * code point, encoded as TBD */ public static long fold(int cp) { var fold = getDefined(cp); return fold == -1 ? cp : fold; } public static boolean isSingleCodePoint(long fold) { return (fold >> 48) == 0; } /** * Returns an expansion set to "close" a given regex Unicode character class range for case-sensitive * matching, according to the * <a href="https://www.unicode.org/reports/tr18/#Simple_Loose_Matches">Simple Loose Matches</a> * rule defined in Unicode Technical Standard #18: Unicode Regular Expressions. * <p> * To conform with Level 1 of UTS #18, specifically RL1.5: Simple Loose Matches, simple case folding must * be applied to literals and (optionally) to character classes. When applied to character classes, each * character class is expected to be closed under simple case folding. See the standard for the * detailed explanation and example of "closed". * <p> * RL1.5 states: To meet this requirement, an implementation that supports case-sensitive matching should * <ol> * <li>Provide at least the simple, default Unicode case-insensitive matching, and</li> * <li>Specify which character properties or constructs are closed under the matching.</li> * </ol> * <p> * In the {@code Pattern} implementation, 5 types of constructs maybe case-sensitive when matching: * back-refs, string slice (sequences), single, family(char-property) and class range. Single and * family may appears independently or within a class. * <p> * For loose/case-insensitive matching, the back-refs, slices and singles apply {@code toUpperCase} and * {@code toLowerCase} to both the pattern and the input string. This effectively 'close' the class for * matching. * <p> * The family/char-properties are not "closed" and should remain unchanged. This is acceptable per RL1.5, * if their behavior is clearly specified. * <p> * This method addresses that requirement for the "range" construct within in character class by computing * the additional characters that should be included to close the range under simple case folding: * <p> * For each character in the input range {@code [start, end]} (inclusive), if the character has a simple * case folding mapping in Unicode's CaseFolding.txt, the mapping is not a round-trip map, and the mapped * character is not already in the range, then that mapped character (typically lowercase) is added to * the expansion set. * <p> * This allows regex character class "range" implementation to use the returned expansion set to support * additional case-insensitive matching, without duplicating characters already covered by the existing * regex range implementation. The expectation is the matching is done using both the uppercase and * lowercase forms of the input character, for example * * <pre>{@code * * ch -> inRange(lower, Character.toUpperCase(ch), upper) || * inRange(lower, Character.toLower(ch), upper) || * additionalClosingCharacters.contains(Character.toUpperCase(ch)) || * additionalClosingCharacters.contains(Character.toUpperCase(ch)) * }</pre> * * @param start the starting code point of the character range * @param end the ending code point of the character range * @return a {@code int[]} containing the all simple case equivalents of characters in the range, excluding * those already in the range * @spec https://www.unicode.org/reports/tr18/#Simple_Loose_Matches */ public static int[] getClassRangeClosingCharacters(int start, int end) { int[] expanded = new int[expanded_case_cps.length]; int off = 0; for (int cp : expanded_case_cps) { if (cp >= start && cp <= end) { int folding = expanded_case_map.get(cp); if (folding < start || folding > end) { expanded[off++] = folding; } } } return Arrays.copyOf(expanded, off); } private static final Map<Integer, Integer> expanded_case_map = Map.ofEntries( %%%Expanded_Case_Map_Entries ); private static final int[] expanded_case_cps = expanded_case_map.keySet() .stream() .mapToInt(Integer::intValue) .toArray(); private static final int HASH_CP = 0; private static final int HASH_INDEX = 1; private static final int HASH_NEXT = 2; private static int[][] hashKeys(int[] keys) { var hashes = new int[keys.length << 1][3]; // cp + hash + next var off = keys.length; for (int i = 0; i < keys.length; i++) { var cp = keys[i]; var hash = cp % keys.length; while (hashes[hash][HASH_CP] != 0) { var next = hashes[hash][HASH_NEXT]; if (next == 0) { hashes[hash][HASH_NEXT] = off; hash = off++; break; } else { hash = next; } } hashes[hash][HASH_CP] = cp; hashes[hash][HASH_INDEX] = i; } return Arrays.copyOf(hashes, off); } private static long getDefined(int cp) { // Exclude code point U+0000, which is guaranteed to have no // case-folding mapping. if (cp == 0) { return -1; } var hashes = CASE_FOLDING_HASHES; var length = CASE_FOLDING_CPS.length; // hashed based on total defined. var hash = cp % length; while (hashes[hash][HASH_CP] != cp) { var next = hashes[hash][HASH_NEXT]; if (next == 0) { return -1; // hash miss } hash = next; } var index = hashes[hash][HASH_INDEX]; return CASE_FOLDING_VALUES[index]; } %%%Entries private static final int[][] CASE_FOLDING_HASHES = hashKeys(CASE_FOLDING_CPS); }