Skip to content

Commit

Permalink
Reduce String/StringBuilder allocations (#984)
Browse files Browse the repository at this point in the history
* Reduce String/StringBuilder allocations on ConfigMappings::mapConfiguration

* Filter environment variables to be mapped with mappings roots

* Cache property names during mapping

* Avoid exception allocation when checking for indexed segment

---------

Co-authored-by: Roberto Cortez <radcortez@yahoo.com>
  • Loading branch information
franz1981 and radcortez committed Aug 30, 2023
1 parent c519eb7 commit d7060e4
Show file tree
Hide file tree
Showing 5 changed files with 311 additions and 134 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,62 @@ public static String[] split(String text) {
return list.toArray(NO_STRINGS);
}

private static boolean isAsciiLetterOrDigit(char c) {
return 'a' <= c && c <= 'z' ||
'A' <= c && c <= 'Z' ||
'0' <= c && c <= '9';
}

private static boolean isAsciiUpperCase(char c) {
return c >= 'A' && c <= 'Z';
}

private static char toAsciiLowerCase(char c) {
return isAsciiUpperCase(c) ? (char) (c + 32) : c;
}

public static boolean equalsIgnoreCaseReplacingNonAlphanumericByUnderscores(final String property,
CharSequence mappedProperty) {
int length = mappedProperty.length();
if (property.length() != mappedProperty.length()) {
// special-case/slow-path
if (length == 0 || property.length() != mappedProperty.length() + 1) {
return false;
}
if (mappedProperty.charAt(length - 1) == '"' &&
property.charAt(length - 1) == '_' && property.charAt(length) == '_') {
length = mappedProperty.length() - 1;
} else {
return false;
}
}
for (int i = 0; i < length; i++) {
char ch = mappedProperty.charAt(i);
if (!isAsciiLetterOrDigit(ch)) {
if (property.charAt(i) != '_') {
return false;
}
continue;
}
final char pCh = property.charAt(i);
// in theory property should be ascii too, but better play safe
if (pCh < 128) {
if (toAsciiLowerCase(pCh) != toAsciiLowerCase(ch)) {
return false;
}
} else if (Character.toLowerCase(property.charAt(i)) != Character.toLowerCase(ch)) {
return false;
}
}
return true;
}

public static String replaceNonAlphanumericByUnderscores(final String name) {
int length = name.length();
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char c = name.charAt(i);
if ('a' <= c && c <= 'z' ||
'A' <= c && c <= 'Z' ||
'0' <= c && c <= '9') {
if (isAsciiLetterOrDigit(c)) {
sb.append(c);
} else {
sb.append('_');
Expand All @@ -117,51 +165,48 @@ public static String toLowerCaseAndDotted(final String name) {
if ('_' == c) {
if (i == 0) {
// leading _ can only mean a profile
sb.append("%");
sb.append('%');
continue;
}

// Do not convert to index if the first segment is a number
if (beginSegment > 0) {
try {
String segment = sb.substring(beginSegment, i);
Integer.parseInt(segment);
sb.replace(beginSegment - 1, beginSegment, "[").append("]");
if (isNumeric(sb, beginSegment, i)) {
sb.setCharAt(beginSegment - 1, '[');
sb.append(']');

int j = i + 1;
if (j < length) {
if ('_' == name.charAt(j)) {
sb.append(".");
sb.append('.');
i = j;
}
}

continue;
} catch (NumberFormatException e) {
// Ignore, it is not an indexed number
}
}

int j = i + 1;
if (j < length) {
if ('_' == name.charAt(j) && !quotesOpen) {
sb.append(".");
sb.append("\"");
sb.append('.');
sb.append('\"');
i = j;
quotesOpen = true;
} else if ('_' == name.charAt(j) && quotesOpen) {
sb.append("\"");
sb.append('\"');
// Ending
if (j + 1 < length) {
sb.append(".");
sb.append('.');
}
i = j;
quotesOpen = false;
} else {
sb.append(".");
sb.append('.');
}
} else {
sb.append(".");
sb.append('.');
}
beginSegment = j;
} else {
Expand All @@ -171,6 +216,23 @@ public static String toLowerCaseAndDotted(final String name) {
return sb.toString();
}

public static boolean isNumeric(CharSequence digits) {
return isNumeric(digits, 0, digits.length());
}

public static boolean isNumeric(CharSequence digits, int start, int end) {
if (digits.length() == 0) {
return false;
}

for (int i = start; i < end; i++) {
if (!Character.isDigit(digits.charAt(i))) {
return false;
}
}
return true;
}

public static String skewer(String camelHumps) {
return skewer(camelHumps, '-');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
package io.smallrye.config.common.utils;

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;

import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -122,4 +124,12 @@ void replaceNonAlphanumericByUnderscores() {
void toLowerCaseAndDotted() {
assertEquals("test.language.\"de.etr\"", StringUtil.toLowerCaseAndDotted("TEST_LANGUAGE__DE_ETR__"));
}

@Test
void isNumeric() {
assertTrue(StringUtil.isNumeric("0"));
assertFalse(StringUtil.isNumeric("false"));
assertTrue(StringUtil.isNumeric("foo[0]", 4, 5));
assertTrue(StringUtil.isNumeric(new StringBuilder("foo[0]"), 4, 5));
}
}
Loading

0 comments on commit d7060e4

Please sign in to comment.