From fa4c12cdc2547ad74ff1af6bcb4e7634e9d8a239 Mon Sep 17 00:00:00 2001
From: Victor Chang <vichang@google.com>
Date: Mon, 29 Mar 2021 23:52:43 +0100
Subject: [PATCH] ICU-21567 Avoid using regex in ULocale.getName()

No behavior change is expected
---
 .../core/src/com/ibm/icu/util/ULocale.java    | 35 ++++++++++++++++---
 1 file changed, 31 insertions(+), 4 deletions(-)

diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/ULocale.java b/icu4j/main/classes/core/src/com/ibm/icu/util/ULocale.java
index 84472f5b301..c515ca1942b 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/util/ULocale.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/util/ULocale.java
@@ -28,7 +28,6 @@ import java.util.Objects;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
-import java.util.regex.Pattern;
 
 import com.ibm.icu.impl.CacheBase;
 import com.ibm.icu.impl.ICUData;
@@ -119,8 +118,6 @@ public final class ULocale implements Serializable, Comparable<ULocale> {
     // using serialver from jdk1.4.2_05
     private static final long serialVersionUID = 3715177670352309217L;
 
-    private static final Pattern UND_PATTERN = Pattern.compile("^und(?=$|[_-])", Pattern.CASE_INSENSITIVE);
-
     private static CacheBase<String, String, Void> nameCache = new SoftCache<String, String, Void>() {
         @Override
         protected String createInstance(String tmpLocaleID, Void unused) {
@@ -1144,11 +1141,41 @@ public final class ULocale implements Serializable, Comparable<ULocale> {
         } else if ("root".equalsIgnoreCase(localeID)) {
             tmpLocaleID = EMPTY_STRING;
         } else {
-            tmpLocaleID = UND_PATTERN.matcher(localeID).replaceFirst(EMPTY_STRING);
+            tmpLocaleID = stripLeadingUnd(localeID);
         }
         return nameCache.getInstance(tmpLocaleID, null /* unused */);
     }
 
+    /**
+     * Strips out the leading "und" language code case-insensitively.
+     *
+     * @implNote Avoids creating new local non-primitive objects to reduce GC pressure.
+     */
+    private static String stripLeadingUnd(String localeID) {
+        int length = localeID.length();
+        if (length < 3) {
+            return localeID;
+        }
+
+        // If not starts with "und", return.
+        if (!localeID.regionMatches(/*ignoreCase=*/true, 0, "und", 0, /*len=*/3)) {
+            return localeID;
+        }
+
+        // The string is equals to "und" case-insensitively.
+        if (length == 3) {
+            return EMPTY_STRING;
+        }
+
+        // localeID must have a length >= 4
+        char separator = localeID.charAt(3);
+        if (separator == '-' || separator == '_') { // "und-*" or "und_*"
+            return localeID.substring(3);
+        }
+
+        return localeID;
+    }
+
     /**
      * Returns a string representation of this object.
      * @return a string representation of the object.
-- 
2.40.0