From fea37843174906efb5efdce83dce9a2528d2c44e Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Wed, 5 Oct 2022 11:59:59 +0100 Subject: [PATCH 01/10] Refactor & tidy up uses of Strings class --- .../org/elasticsearch/common/Strings.java | 130 ++++++++---------- .../ml/job/process/normalizer/Normalizer.java | 14 +- .../scalar/string/StringFunctionUtils.java | 38 ----- .../string/SubstringFunctionProcessor.java | 11 +- 4 files changed, 72 insertions(+), 121 deletions(-) delete mode 100644 x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/string/StringFunctionUtils.java diff --git a/server/src/main/java/org/elasticsearch/common/Strings.java b/server/src/main/java/org/elasticsearch/common/Strings.java index 3500779b00aed..d3302aa062c97 100644 --- a/server/src/main/java/org/elasticsearch/common/Strings.java +++ b/server/src/main/java/org/elasticsearch/common/Strings.java @@ -28,6 +28,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.StringTokenizer; import java.util.TreeSet; @@ -40,12 +41,11 @@ public class Strings { public static final String[] EMPTY_ARRAY = new String[0]; public static void spaceify(int spaces, String from, StringBuilder to) throws Exception { + assert spaces >= 0; try (BufferedReader reader = new BufferedReader(new StringReader(from))) { String line; while ((line = reader.readLine()) != null) { - for (int i = 0; i < spaces; i++) { - to.append(' '); - } + to.append(" ".repeat(spaces)); to.append(line).append('\n'); } } @@ -126,7 +126,7 @@ public static List splitSmart(String s, String separator, boolean decode * @see #hasText(String) */ public static boolean hasLength(CharSequence str) { - return (str != null && str.length() > 0); + return (str != null && str.isEmpty() == false); } /** @@ -191,13 +191,7 @@ public static boolean hasText(CharSequence str) { if (hasLength(str) == false) { return false; } - int strLen = str.length(); - for (int i = 0; i < strLen; i++) { - if (Character.isWhitespace(str.charAt(i)) == false) { - return true; - } - } - return false; + return str.chars().anyMatch(c -> Character.isWhitespace(c) == false); } /** @@ -225,11 +219,11 @@ public static String trimLeadingCharacter(String str, char leadingCharacter) { if (hasLength(str) == false) { return str; } - StringBuilder sb = new StringBuilder(str); - while (sb.length() > 0 && sb.charAt(0) == leadingCharacter) { - sb.deleteCharAt(0); + int i = 0; + while (i < str.length() && str.charAt(i) == leadingCharacter) { + i++; } - return sb.toString(); + return str.substring(i); } /** @@ -269,7 +263,7 @@ public static String replace(String inString, String oldPattern, String newPatte // the index of an occurrence we've found, or -1 int patLen = oldPattern.length(); while (index >= 0) { - sb.append(inString.substring(pos, index)); + sb.append(inString, pos, index); sb.append(newPattern); pos = index + patLen; index = inString.indexOf(oldPattern, pos); @@ -288,17 +282,29 @@ public static String replace(String inString, String oldPattern, String newPatte * @return the resulting String */ public static String deleteAny(String inString, String charsToDelete) { + return deleteAny((CharSequence) inString, charsToDelete).toString(); + } + + /** + * Delete any character in a given CharSequence. + * + * @param inString the original CharSequence + * @param charsToDelete a set of characters to delete. + * E.g. "az\n" will delete 'a's, 'z's and new lines. + * @return the resulting CharSequence + */ + public static CharSequence deleteAny(CharSequence inString, String charsToDelete) { if (hasLength(inString) == false || hasLength(charsToDelete) == false) { return inString; } - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(inString.length()); for (int i = 0; i < inString.length(); i++) { char c = inString.charAt(i); if (charsToDelete.indexOf(c) == -1) { sb.append(c); } } - return sb.toString(); + return sb; } // --------------------------------------------------------------------- @@ -321,37 +327,24 @@ private static String changeFirstCharacterCase(String str, boolean capitalize) { if (str == null || str.length() == 0) { return str; } - StringBuilder sb = new StringBuilder(str.length()); - if (capitalize) { - sb.append(Character.toUpperCase(str.charAt(0))); - } else { - sb.append(Character.toLowerCase(str.charAt(0))); + char newChar = capitalize ? Character.toUpperCase(str.charAt(0)) : Character.toLowerCase(str.charAt(0)); + if (newChar == str.charAt(0)) { + return str; // nothing changed } - sb.append(str.substring(1)); - return sb.toString(); + + return newChar + str.substring(1); } - public static final String INVALID_FILENAME_CHARS = "[" - + Stream.of('\\', '/', '*', '?', '"', '<', '>', '|', ' ', ',').map(c -> "'" + c + "'").collect(Collectors.joining(",")) - + "]"; + public static final String INVALID_FILENAME_CHARS = Stream.of('\\', '/', '*', '?', '"', '<', '>', '|', ' ', ',') + .map(c -> "'" + c + "'") + .collect(Collectors.joining(",", "[", "]")); public static boolean validFileName(String fileName) { - for (int i = 0; i < fileName.length(); i++) { - if (isInvalidFileNameCharacter(fileName.charAt(i))) { - return false; - } - } - return true; + return fileName.chars().noneMatch(c -> isInvalidFileNameCharacter((char) c)); } public static boolean validFileNameExcludingAstrix(String fileName) { - for (int i = 0; i < fileName.length(); i++) { - char c = fileName.charAt(i); - if (c != '*' && isInvalidFileNameCharacter(c)) { - return false; - } - } - return true; + return fileName.chars().noneMatch(c -> c != '*' && isInvalidFileNameCharacter((char) c)); } private static boolean isInvalidFileNameCharacter(char c) { @@ -373,7 +366,7 @@ public static String[] toStringArray(Collection collection) { if (collection == null) { return null; } - return collection.toArray(new String[collection.size()]); + return collection.toArray(String[]::new); } /** @@ -531,12 +524,20 @@ public static String[] delimitedListToStringArray(String str, String delimiter, if (delimiter == null) { return new String[] { str }; } - List result = new ArrayList<>(); - if ("".equals(delimiter)) { + List result; + if (delimiter.isEmpty()) { + // split on every character + result = new ArrayList<>(str.length()); + if (charsToDelete == null) { + charsToDelete = ""; + } for (int i = 0; i < str.length(); i++) { - result.add(deleteAny(str.substring(i, i + 1), charsToDelete)); + if (charsToDelete.indexOf(str.charAt(i)) == -1) { + result.add(Character.toString(str.charAt(i))); + } } } else { + result = new ArrayList<>(); int pos = 0; int delPos; while ((delPos = str.indexOf(delimiter, pos)) != -1) { @@ -545,7 +546,7 @@ public static String[] delimitedListToStringArray(String str, String delimiter, } if (str.length() > 0 && pos <= str.length()) { // Add rest of String, but not in case of empty input. - result.add(deleteAny(str.substring(pos), charsToDelete)); + result.add(deleteAny(str.subSequence(pos, str.length()), charsToDelete).toString()); } } return toStringArray(result); @@ -569,10 +570,8 @@ public static String[] commaDelimitedListToStringArray(String str) { * @return a Set of String entries in the list */ public static Set commaDelimitedListToSet(String str) { - Set set = new TreeSet<>(); String[] tokens = commaDelimitedListToStringArray(str); - set.addAll(Arrays.asList(tokens)); - return set; + return new TreeSet<>(Arrays.asList(tokens)); } /** @@ -917,38 +916,27 @@ public static boolean isNullOrBlank(@Nullable String s) { return s == null || s.isBlank(); } + /** + * @deprecated Use RequireNonNullElse instead + */ + @Deprecated public static String coalesceToEmpty(@Nullable String s) { - return s == null ? "" : s; + return Objects.requireNonNullElse(s, ""); } public static String padStart(String s, int minimumLength, char c) { - if (s == null) { - throw new NullPointerException("s"); - } + Objects.requireNonNull(s, "s"); if (s.length() >= minimumLength) { return s; } else { - StringBuilder sb = new StringBuilder(minimumLength); - for (int i = s.length(); i < minimumLength; i++) { - sb.append(c); - } - - sb.append(s); - return sb.toString(); + return Character.toString(c).repeat(minimumLength - s.length()) + s; } } public static String toLowercaseAscii(String in) { - StringBuilder out = new StringBuilder(); - Iterator iter = in.codePoints().iterator(); - while (iter.hasNext()) { - int codepoint = iter.next(); - if (codepoint > 128) { - out.appendCodePoint(codepoint); - } else { - out.appendCodePoint(Character.toLowerCase(codepoint)); - } - } - return out.toString(); + return in.codePoints() + .map(cp -> cp <= 128 ? Character.toLowerCase(cp) : cp) + .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) + .toString(); } } diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/normalizer/Normalizer.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/normalizer/Normalizer.java index 7d61cbdb1c98d..916b59b0558fa 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/normalizer/Normalizer.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/normalizer/Normalizer.java @@ -9,12 +9,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.common.Strings; import org.elasticsearch.xpack.ml.job.process.normalizer.output.NormalizerResultHandler; import java.io.IOException; import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; @@ -104,12 +104,12 @@ private static void writeNormalizableAndChildrenRecursively(Normalizable normali process.writeRecord( new String[] { normalizable.getLevel().asString(), - Strings.coalesceToEmpty(normalizable.getPartitionFieldName()), - Strings.coalesceToEmpty(normalizable.getPartitionFieldValue()), - Strings.coalesceToEmpty(normalizable.getPersonFieldName()), - Strings.coalesceToEmpty(normalizable.getPersonFieldValue()), - Strings.coalesceToEmpty(normalizable.getFunctionName()), - Strings.coalesceToEmpty(normalizable.getValueFieldName()), + Objects.requireNonNullElse(normalizable.getPartitionFieldName(), ""), + Objects.requireNonNullElse(normalizable.getPartitionFieldValue(), ""), + Objects.requireNonNullElse(normalizable.getPersonFieldName(), ""), + Objects.requireNonNullElse(normalizable.getPersonFieldValue(), ""), + Objects.requireNonNullElse(normalizable.getFunctionName(), ""), + Objects.requireNonNullElse(normalizable.getValueFieldName(), ""), Double.toString(normalizable.getProbability()), Double.toString(normalizable.getNormalizedScore()) } ); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/string/StringFunctionUtils.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/string/StringFunctionUtils.java deleted file mode 100644 index 406520787f3dd..0000000000000 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/string/StringFunctionUtils.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -package org.elasticsearch.xpack.sql.expression.function.scalar.string; - -import static org.elasticsearch.common.Strings.hasLength; - -final class StringFunctionUtils { - - private StringFunctionUtils() {} - - /** - * Extract a substring from the given string, using start index and length of the extracted substring. - * - * @param s the original String - * @param start starting position for the substring within the original string. 0-based index position - * @param length length in characters of the subtracted substring - * @return the resulting String - */ - static String substring(String s, int start, int length) { - if (hasLength(s) == false) { - return s; - } - - if (start < 0) { - start = 0; - } - - if (start + 1 > s.length() || length < 0) { - return ""; - } - - return (start + length > s.length()) ? s.substring(start) : s.substring(start, start + length); - } -} diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/string/SubstringFunctionProcessor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/string/SubstringFunctionProcessor.java index f31264350b069..8fe0bca193a3d 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/string/SubstringFunctionProcessor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/string/SubstringFunctionProcessor.java @@ -6,6 +6,7 @@ */ package org.elasticsearch.xpack.sql.expression.function.scalar.string; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.xpack.ql.expression.gen.processor.Processor; @@ -59,11 +60,11 @@ public static Object doProcess(Object input, Object start, Object length) { Check.isFixedNumberAndInRange(start, "start", (long) Integer.MIN_VALUE + 1, (long) Integer.MAX_VALUE); Check.isFixedNumberAndInRange(length, "length", 0L, (long) Integer.MAX_VALUE); - return StringFunctionUtils.substring( - input instanceof Character ? input.toString() : (String) input, - ((Number) start).intValue() - 1, // SQL is 1-based when it comes to string manipulation - ((Number) length).intValue() - ); + String s = input instanceof Character ? input.toString() : (String) input; + int strStart = ((Number) start).intValue() - 1; // SQL is 1-based when it comes to string manipulation + strStart = Math.min(Math.max(0, strStart), s.length()); // sanitise string start index + + return Strings.substring(s, strStart, strStart + ((Number) length).intValue()); } protected Processor input() { From 08e43f78bf36fbfc0855930277ada003acd54eb6 Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Wed, 5 Oct 2022 13:33:13 +0100 Subject: [PATCH 02/10] Add some unit tests to string methods --- .../org/elasticsearch/common/Strings.java | 13 +++--- .../elasticsearch/common/StringsTests.java | 46 +++++++++++++++++++ 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/Strings.java b/server/src/main/java/org/elasticsearch/common/Strings.java index d3302aa062c97..6b0c4c4fc0f6a 100644 --- a/server/src/main/java/org/elasticsearch/common/Strings.java +++ b/server/src/main/java/org/elasticsearch/common/Strings.java @@ -40,13 +40,14 @@ public class Strings { public static final String[] EMPTY_ARRAY = new String[0]; - public static void spaceify(int spaces, String from, StringBuilder to) throws Exception { - assert spaces >= 0; + public static void spaceify(int spaces, String from, StringBuilder to) throws IOException { + char[] spaceChars = new char[spaces]; + Arrays.fill(spaceChars, ' '); + try (BufferedReader reader = new BufferedReader(new StringReader(from))) { String line; while ((line = reader.readLine()) != null) { - to.append(" ".repeat(spaces)); - to.append(line).append('\n'); + to.append(spaceChars).append(line).append('\n'); } } } @@ -70,7 +71,7 @@ public static List splitSmart(String s, String separator, boolean decode if (s.startsWith(separator, pos)) { if (sb.length() > 0) { lst.add(sb.toString()); - sb = new StringBuilder(); + sb.setLength(0); } pos += separator.length(); continue; @@ -541,7 +542,7 @@ public static String[] delimitedListToStringArray(String str, String delimiter, int pos = 0; int delPos; while ((delPos = str.indexOf(delimiter, pos)) != -1) { - result.add(deleteAny(str.substring(pos, delPos), charsToDelete)); + result.add(deleteAny(str.subSequence(pos, delPos), charsToDelete).toString()); pos = delPos + delimiter.length(); } if (str.length() > 0 && pos <= str.length()) { diff --git a/server/src/test/java/org/elasticsearch/common/StringsTests.java b/server/src/test/java/org/elasticsearch/common/StringsTests.java index ef005df4eaac7..593c89ffcc89b 100644 --- a/server/src/test/java/org/elasticsearch/common/StringsTests.java +++ b/server/src/test/java/org/elasticsearch/common/StringsTests.java @@ -13,12 +13,17 @@ import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.ToXContentObject; +import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.emptyArray; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -26,6 +31,19 @@ public class StringsTests extends ESTestCase { + public void testSpaceify() throws IOException { + String[] lines = new String[] { randomAlphaOfLength(5), randomAlphaOfLength(5), randomAlphaOfLength(5) }; + + // spaceify always finishes with \n regardless of input + StringBuilder sb = new StringBuilder(); + Strings.spaceify(4, String.join("\n", lines), sb); + assertThat(sb.toString(), equalTo(Arrays.stream(lines).map(s -> " ".repeat(4) + s).collect(Collectors.joining("\n", "", "\n")))); + + sb = new StringBuilder(); + Strings.spaceify(0, String.join("\n", lines), sb); + assertThat(sb.toString(), equalTo(Arrays.stream(lines).collect(Collectors.joining("\n", "", "\n")))); + } + public void testIsAllOrWildCardString() { assertThat(Strings.isAllOrWildcard("_all"), is(true)); assertThat(Strings.isAllOrWildcard("*"), is(true)); @@ -62,6 +80,11 @@ public void testCleanTruncate() { assertEquals("", Strings.cleanTruncate("foo", 0)); } + public void testTrimLeadingCharacter() { + assertThat(Strings.trimLeadingCharacter("abcdef", 'g'), equalTo("abcdef")); + assertThat(Strings.trimLeadingCharacter("aaabcdef", 'a'), equalTo("bcdef")); + } + public void testToStringToXContent() { final ToXContent toXContent; final boolean error; @@ -120,6 +143,21 @@ public void testSplitStringToSet() { assertEquals(Strings.tokenizeByCommaToSet(" "), Sets.newHashSet()); } + public void testDelimitedListToStringArray() { + String testStr; + assertThat(Strings.delimitedListToStringArray(null, " ", "a"), emptyArray()); + // NOTE: current behaviour is to not delete anything if the delimiter is null + assertThat(Strings.delimitedListToStringArray(testStr = randomAlphaOfLength(10), null, "a"), arrayContaining(testStr)); + assertThat( + Strings.delimitedListToStringArray(testStr = randomAlphaOfLength(10), "", null), + arrayContaining(testStr.chars().mapToObj(Character::toString).toArray()) + ); + assertThat(Strings.delimitedListToStringArray("abcdabceabcdf", "", "da"), arrayContaining("b", "c", "b", "c", "e", "b", "c", "f")); + assertThat(Strings.delimitedListToStringArray("abcd,abce,abcdf", ",", "da"), arrayContaining("bc", "bce", "bcf")); + assertThat(Strings.delimitedListToStringArray("abcd,abce,abcdf,", ",", "da"), arrayContaining("bc", "bce", "bcf", "")); + assertThat(Strings.delimitedListToStringArray("abcd,abce,abcdf,bcad,a", ",a", "d"), arrayContaining("abc", "bce", "bcf,bca", "")); + } + public void testCollectionToDelimitedStringWithLimitZero() { final String delimiter = randomFrom("", ",", ", ", "/"); final String prefix = randomFrom("", "["); @@ -202,4 +240,12 @@ public void testCollectionToDelimitedStringWithLimitNoTruncation() { Strings.collectionToDelimitedStringWithLimit(strings, delimiter, prefix, suffix, limit, stringBuilder); assertThat(stringBuilder.toString(), equalTo(fullDescription)); } + + public void testPadStart() { + String testStr; + assertThat(Strings.padStart("", 5, 'a'), equalTo("aaaaa")); + assertThat(Strings.padStart(testStr = randomAlphaOfLength(6), 10, ' '), equalTo(" ".repeat(4) + testStr)); + assertThat(Strings.padStart(testStr = randomAlphaOfLength(6), 5, ' '), equalTo(testStr)); + assertThat(Strings.padStart(testStr = randomAlphaOfLength(6), 10, 'f'), equalTo("f".repeat(4) + testStr)); + } } From af4e84206fdec0fe1d5bdefdcea8ec76440b2d60 Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Wed, 5 Oct 2022 13:39:49 +0100 Subject: [PATCH 03/10] Refine behaviour of delimitedListToStringArray --- .../java/org/elasticsearch/common/Strings.java | 2 ++ .../org/elasticsearch/common/StringsTests.java | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/Strings.java b/server/src/main/java/org/elasticsearch/common/Strings.java index 6b0c4c4fc0f6a..9da3139d898dc 100644 --- a/server/src/main/java/org/elasticsearch/common/Strings.java +++ b/server/src/main/java/org/elasticsearch/common/Strings.java @@ -535,6 +535,8 @@ public static String[] delimitedListToStringArray(String str, String delimiter, for (int i = 0; i < str.length(); i++) { if (charsToDelete.indexOf(str.charAt(i)) == -1) { result.add(Character.toString(str.charAt(i))); + } else { + result.add(""); } } } else { diff --git a/server/src/test/java/org/elasticsearch/common/StringsTests.java b/server/src/test/java/org/elasticsearch/common/StringsTests.java index 593c89ffcc89b..43d9d086ebefa 100644 --- a/server/src/test/java/org/elasticsearch/common/StringsTests.java +++ b/server/src/test/java/org/elasticsearch/common/StringsTests.java @@ -13,7 +13,6 @@ import org.elasticsearch.xcontent.ToXContent; import org.elasticsearch.xcontent.ToXContentObject; -import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -31,7 +30,7 @@ public class StringsTests extends ESTestCase { - public void testSpaceify() throws IOException { + public void testSpaceify() throws Exception { String[] lines = new String[] { randomAlphaOfLength(5), randomAlphaOfLength(5), randomAlphaOfLength(5) }; // spaceify always finishes with \n regardless of input @@ -152,7 +151,18 @@ public void testDelimitedListToStringArray() { Strings.delimitedListToStringArray(testStr = randomAlphaOfLength(10), "", null), arrayContaining(testStr.chars().mapToObj(Character::toString).toArray()) ); - assertThat(Strings.delimitedListToStringArray("abcdabceabcdf", "", "da"), arrayContaining("b", "c", "b", "c", "e", "b", "c", "f")); + assertThat( + Strings.delimitedListToStringArray("bcdabceabcdf", "", "a"), + arrayContaining("b", "c", "d", "", "b", "c", "e", "", "b", "c", "d", "f") + ); + assertThat( + Strings.delimitedListToStringArray("bcdabceabcdf", "", "da"), + arrayContaining("b", "c", "", "", "b", "c", "e", "", "b", "c", "", "f") + ); + assertThat( + Strings.delimitedListToStringArray("abcdabceabcdf", "", "da"), + arrayContaining("", "b", "c", "", "", "b", "c", "e", "", "b", "c", "", "f") + ); assertThat(Strings.delimitedListToStringArray("abcd,abce,abcdf", ",", "da"), arrayContaining("bc", "bce", "bcf")); assertThat(Strings.delimitedListToStringArray("abcd,abce,abcdf,", ",", "da"), arrayContaining("bc", "bce", "bcf", "")); assertThat(Strings.delimitedListToStringArray("abcd,abce,abcdf,bcad,a", ",a", "d"), arrayContaining("abc", "bce", "bcf,bca", "")); From 3e721daceef0a972609bbad4759bc8480b04b7b3 Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Mon, 10 Oct 2022 13:29:16 +0100 Subject: [PATCH 04/10] Add some more unit tests --- plugins/untitled/src/Main.java | 78 ++++++++ .../org/elasticsearch/common/Strings.java | 5 +- .../elasticsearch/common/StringsTests.java | 172 +++++++++++++----- .../xpack/sql/qa/cli/EmbeddedCli.java | 4 +- 4 files changed, 206 insertions(+), 53 deletions(-) create mode 100644 plugins/untitled/src/Main.java diff --git a/plugins/untitled/src/Main.java b/plugins/untitled/src/Main.java new file mode 100644 index 0000000000000..98612959471cb --- /dev/null +++ b/plugins/untitled/src/Main.java @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.nio.ByteBuffer; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.Arrays; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +public class Main { + public static void main(String[] args) throws Exception { + rsa2048(); + } + + public static void aes256() throws Exception { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(256); + SecretKey secretKey = keyGenerator.generateKey(); + byte[] data = new byte[1024*1024*50]; + + Random r = new Random(); + r.nextBytes(data); + + Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding"); + c.init(Cipher.ENCRYPT_MODE, secretKey); + byte[] encrypt = new byte[c.getOutputSize(data.length)]; + + long ns = System.nanoTime(); + c.doFinal(ByteBuffer.wrap(data), ByteBuffer.wrap(encrypt)); + System.out.println(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - ns) + "ms"); + + c.init(Cipher.DECRYPT_MODE, secretKey); + data = new byte[c.getOutputSize(encrypt.length)]; + + ns = System.nanoTime(); + c.doFinal(ByteBuffer.wrap(encrypt), ByteBuffer.wrap(data)); + System.out.println(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - ns) + "ms"); + } + + public static void rsa2048() throws Exception { + long ns = System.nanoTime(); + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(2048); + KeyPair keypair = keyGen.genKeyPair(); + System.out.println("Generation: " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - ns) + "ms"); + PrivateKey privateKey = keypair.getPrivate(); + PublicKey publicKey = keypair.getPublic(); + + byte[] data = new byte[500]; + byte[] encrypt = new byte[500]; + + Random r = new Random(); + r.nextBytes(data); + + ns = System.nanoTime(); + Cipher c = Cipher.getInstance("RSA"); + + int w=0; + //for (int b=0; b<1024*1024*5; b += 200) { + c.init(Cipher.ENCRYPT_MODE, publicKey); + w += c.doFinal(data, 0, 200, encrypt, w); + //} + System.out.println("Encryption: " + (System.nanoTime() - ns) + "ns"); + + } +} diff --git a/server/src/main/java/org/elasticsearch/common/Strings.java b/server/src/main/java/org/elasticsearch/common/Strings.java index 9da3139d898dc..e477128d5727a 100644 --- a/server/src/main/java/org/elasticsearch/common/Strings.java +++ b/server/src/main/java/org/elasticsearch/common/Strings.java @@ -63,12 +63,13 @@ public static void spaceify(int spaces, String from, StringBuilder to) throws IO * @param separator the separator to split on * @param decode decode backslash escaping */ + @Deprecated // this method is too complicated for its own good public static List splitSmart(String s, String separator, boolean decode) { ArrayList lst = new ArrayList<>(2); StringBuilder sb = new StringBuilder(); int pos = 0, end = s.length(); while (pos < end) { - if (s.startsWith(separator, pos)) { + if (separator.isEmpty() == false && s.startsWith(separator, pos)) { if (sb.length() > 0) { lst.add(sb.toString()); sb.setLength(0); @@ -283,7 +284,7 @@ public static String replace(String inString, String oldPattern, String newPatte * @return the resulting String */ public static String deleteAny(String inString, String charsToDelete) { - return deleteAny((CharSequence) inString, charsToDelete).toString(); + return inString != null ? deleteAny((CharSequence) inString, charsToDelete).toString() : null; } /** diff --git a/server/src/test/java/org/elasticsearch/common/StringsTests.java b/server/src/test/java/org/elasticsearch/common/StringsTests.java index 43d9d086ebefa..36fa227a531c8 100644 --- a/server/src/test/java/org/elasticsearch/common/StringsTests.java +++ b/server/src/test/java/org/elasticsearch/common/StringsTests.java @@ -18,15 +18,19 @@ import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.IntStream; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.arrayContaining; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyArray; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.elasticsearch.common.Strings.*; public class StringsTests extends ESTestCase { @@ -35,39 +39,66 @@ public void testSpaceify() throws Exception { // spaceify always finishes with \n regardless of input StringBuilder sb = new StringBuilder(); - Strings.spaceify(4, String.join("\n", lines), sb); + spaceify(4, String.join("\n", lines), sb); assertThat(sb.toString(), equalTo(Arrays.stream(lines).map(s -> " ".repeat(4) + s).collect(Collectors.joining("\n", "", "\n")))); sb = new StringBuilder(); - Strings.spaceify(0, String.join("\n", lines), sb); + spaceify(0, String.join("\n", lines), sb); assertThat(sb.toString(), equalTo(Arrays.stream(lines).collect(Collectors.joining("\n", "", "\n")))); } + public void testSplitSmart() { + String testStr; + assertThat(splitSmart("foobar", "", false), contains("foobar")); + assertThat(splitSmart(testStr = "a\nb\r\nc\td\be\f", "", false), contains(testStr)); + assertThat(splitSmart(testStr = "a\nb\r\nc\td\be\f", "", true), contains(testStr)); + } + + public void testHasLength() { + assertFalse(hasLength((String)null)); + assertFalse(hasLength("")); + assertTrue(hasLength(" ")); + assertTrue(hasLength("Hello")); + assertTrue(hasLength("\0")); + } + + public void testHasText() { + assertFalse(hasText(null)); + assertFalse(hasText("")); + assertFalse(hasText(" ")); + assertTrue(hasText("12345")); + assertTrue(hasText(" 12345 ")); + + String asciiWhitespace = IntStream.rangeClosed(0, 32).filter(Character::isWhitespace).mapToObj(Character::toString).collect(Collectors.joining()); + assertFalse(hasText(asciiWhitespace)); + assertTrue(hasText("\ud855\udddd")); + } + public void testIsAllOrWildCardString() { - assertThat(Strings.isAllOrWildcard("_all"), is(true)); - assertThat(Strings.isAllOrWildcard("*"), is(true)); - assertThat(Strings.isAllOrWildcard("foo"), is(false)); - assertThat(Strings.isAllOrWildcard(""), is(false)); - assertThat(Strings.isAllOrWildcard((String) null), is(false)); + assertThat(isAllOrWildcard("_all"), is(true)); + assertThat(isAllOrWildcard("*"), is(true)); + assertThat(isAllOrWildcard("foo"), is(false)); + assertThat(isAllOrWildcard(""), is(false)); + assertThat(isAllOrWildcard((String) null), is(false)); } public void testSubstring() { - assertEquals(null, Strings.substring(null, 0, 1000)); - assertEquals("foo", Strings.substring("foo", 0, 1000)); - assertEquals("foo", Strings.substring("foo", 0, 3)); - assertEquals("oo", Strings.substring("foo", 1, 3)); - assertEquals("oo", Strings.substring("foo", 1, 100)); - assertEquals("f", Strings.substring("foo", 0, 1)); + assertNull(substring(null, 0, 1000)); + assertEquals("foo", substring("foo", 0, 1000)); + assertEquals("foo", substring("foo", 0, 3)); + assertEquals("oo", substring("foo", 1, 3)); + assertEquals("oo", substring("foo", 1, 100)); + assertEquals("f", substring("foo", 0, 1)); } public void testCleanTruncate() { - assertEquals(null, Strings.cleanTruncate(null, 10)); - assertEquals("foo", Strings.cleanTruncate("foo", 10)); - assertEquals("foo", Strings.cleanTruncate("foo", 3)); + assertNull(cleanTruncate(null, 10)); + assertEquals("foo", cleanTruncate("foo", 10)); + assertEquals("foo", cleanTruncate("foo", 3)); // Throws out high surrogates - assertEquals("foo", Strings.cleanTruncate("foo\uD83D\uDEAB", 4)); + assertEquals("foo", cleanTruncate("foo\uD83D\uDEAB", 4)); // But will keep the whole character - assertEquals("foo\uD83D\uDEAB", Strings.cleanTruncate("foo\uD83D\uDEAB", 5)); + assertEquals("foo\uD83D\uDEAB", cleanTruncate("foo\uD83D\uDEAB", 5)); /* * Doesn't take care around combining marks. This example has its * meaning changed because that last codepoint is supposed to combine @@ -75,13 +106,13 @@ public void testCleanTruncate() { * circle around it with a slash through it. As in "no 'o's allowed * here. */ - assertEquals("o", Strings.cleanTruncate("o\uD83D\uDEAB", 1)); - assertEquals("", Strings.cleanTruncate("foo", 0)); + assertEquals("o", cleanTruncate("o\uD83D\uDEAB", 1)); + assertEquals("", cleanTruncate("foo", 0)); } public void testTrimLeadingCharacter() { - assertThat(Strings.trimLeadingCharacter("abcdef", 'g'), equalTo("abcdef")); - assertThat(Strings.trimLeadingCharacter("aaabcdef", 'a'), equalTo("bcdef")); + assertThat(trimLeadingCharacter("abcdef", 'g'), equalTo("abcdef")); + assertThat(trimLeadingCharacter("aaabcdef", 'a'), equalTo("bcdef")); } public void testToStringToXContent() { @@ -129,43 +160,59 @@ public void testToStringToXContentWithOrWithoutParams() { ); } + public void testDeleteAny() { + assertNull(deleteAny((CharSequence)null, "abc")); + assertNull(deleteAny((String)null, "abc")); + assertThat(deleteAny(new StringBuilder("foo"), null), hasToString("foo")); + assertThat(deleteAny("foo", null), equalTo("foo")); + + assertThat(deleteAny("abc\ndef\t", "az\n"), equalTo("bcdef\t")); + + String testStr = randomUnicodeOfLength(10); + String delete = testStr.substring(testStr.length()-1) + testStr.substring(0, 1); + assertThat(deleteAny(testStr, delete), equalTo(testStr.substring(1, testStr.length()-1))); + assertThat(deleteAny(new StringBuilder(testStr), delete), hasToString(testStr.substring(1, testStr.length()-1))); + + // this method doesn't really work with surrogates + } + public void testSplitStringToSet() { - assertEquals(Strings.tokenizeByCommaToSet(null), Sets.newHashSet()); - assertEquals(Strings.tokenizeByCommaToSet(""), Sets.newHashSet()); - assertEquals(Strings.tokenizeByCommaToSet("a,b,c"), Sets.newHashSet("a", "b", "c")); - assertEquals(Strings.tokenizeByCommaToSet("a, b, c"), Sets.newHashSet("a", "b", "c")); - assertEquals(Strings.tokenizeByCommaToSet(" a , b, c "), Sets.newHashSet("a", "b", "c")); - assertEquals(Strings.tokenizeByCommaToSet("aa, bb, cc"), Sets.newHashSet("aa", "bb", "cc")); - assertEquals(Strings.tokenizeByCommaToSet(" a "), Sets.newHashSet("a")); - assertEquals(Strings.tokenizeByCommaToSet(" a "), Sets.newHashSet("a")); - assertEquals(Strings.tokenizeByCommaToSet(" aa "), Sets.newHashSet("aa")); - assertEquals(Strings.tokenizeByCommaToSet(" "), Sets.newHashSet()); + assertEquals(tokenizeByCommaToSet(null), Sets.newHashSet()); + assertEquals(tokenizeByCommaToSet(""), Sets.newHashSet()); + assertEquals(tokenizeByCommaToSet("a,b,c"), Sets.newHashSet("a", "b", "c")); + assertEquals(tokenizeByCommaToSet("a, b, c"), Sets.newHashSet("a", "b", "c")); + assertEquals(tokenizeByCommaToSet(" a , b, c "), Sets.newHashSet("a", "b", "c")); + assertEquals(tokenizeByCommaToSet("aa, bb, cc"), Sets.newHashSet("aa", "bb", "cc")); + assertEquals(tokenizeByCommaToSet(" a "), Sets.newHashSet("a")); + assertEquals(tokenizeByCommaToSet(" a "), Sets.newHashSet("a")); + assertEquals(tokenizeByCommaToSet(" aa "), Sets.newHashSet("aa")); + assertEquals(tokenizeByCommaToSet(" "), Sets.newHashSet()); } public void testDelimitedListToStringArray() { String testStr; - assertThat(Strings.delimitedListToStringArray(null, " ", "a"), emptyArray()); + assertThat(delimitedListToStringArray(null, " ", "a"), emptyArray()); // NOTE: current behaviour is to not delete anything if the delimiter is null - assertThat(Strings.delimitedListToStringArray(testStr = randomAlphaOfLength(10), null, "a"), arrayContaining(testStr)); + assertThat(delimitedListToStringArray(testStr = randomAlphaOfLength(10), null, "a"), arrayContaining(testStr)); assertThat( - Strings.delimitedListToStringArray(testStr = randomAlphaOfLength(10), "", null), + delimitedListToStringArray(testStr = randomAlphaOfLength(10), "", null), arrayContaining(testStr.chars().mapToObj(Character::toString).toArray()) ); assertThat( - Strings.delimitedListToStringArray("bcdabceabcdf", "", "a"), + delimitedListToStringArray("bcdabceabcdf", "", "a"), arrayContaining("b", "c", "d", "", "b", "c", "e", "", "b", "c", "d", "f") ); assertThat( - Strings.delimitedListToStringArray("bcdabceabcdf", "", "da"), + delimitedListToStringArray("bcdabceabcdf", "", "da"), arrayContaining("b", "c", "", "", "b", "c", "e", "", "b", "c", "", "f") ); assertThat( - Strings.delimitedListToStringArray("abcdabceabcdf", "", "da"), + delimitedListToStringArray("abcdabceabcdf", "", "da"), arrayContaining("", "b", "c", "", "", "b", "c", "e", "", "b", "c", "", "f") ); - assertThat(Strings.delimitedListToStringArray("abcd,abce,abcdf", ",", "da"), arrayContaining("bc", "bce", "bcf")); - assertThat(Strings.delimitedListToStringArray("abcd,abce,abcdf,", ",", "da"), arrayContaining("bc", "bce", "bcf", "")); - assertThat(Strings.delimitedListToStringArray("abcd,abce,abcdf,bcad,a", ",a", "d"), arrayContaining("abc", "bce", "bcf,bca", "")); + assertThat(delimitedListToStringArray("abcd,abce,abcdf", ",", "da"), arrayContaining("bc", "bce", "bcf")); + assertThat(delimitedListToStringArray("abcd,abce,abcdf,", ",", "da"), arrayContaining("bc", "bce", "bcf", "")); + assertThat(delimitedListToStringArray("abcd,abce,abcdf,bcad,a", ",a", "d"), arrayContaining("abc", "bce", "bcf,bca", "")); } public void testCollectionToDelimitedStringWithLimitZero() { @@ -182,7 +229,7 @@ public void testCollectionToDelimitedStringWithLimitZero() { } final StringBuilder stringBuilder = new StringBuilder(); - Strings.collectionToDelimitedStringWithLimit(strings, delimiter, prefix, suffix, 0, stringBuilder); + collectionToDelimitedStringWithLimit(strings, delimiter, prefix, suffix, 0, stringBuilder); final String completelyTruncatedDescription = stringBuilder.toString(); if (count == 0) { @@ -210,11 +257,11 @@ public void testCollectionToDelimitedStringWithLimitTruncation() { strings.add(randomAlphaOfLength(between(minLength, 10))); } - final int fullDescriptionLength = Strings.collectionToDelimitedString(strings, delimiter, prefix, suffix).length(); + final int fullDescriptionLength = collectionToDelimitedString(strings, delimiter, prefix, suffix).length(); final int lastItemSize = prefix.length() + strings.get(count - 1).length() + suffix.length(); final int truncatedLength = between(0, fullDescriptionLength - lastItemSize - 1); final StringBuilder stringBuilder = new StringBuilder(); - Strings.collectionToDelimitedStringWithLimit(strings, delimiter, prefix, suffix, truncatedLength, stringBuilder); + collectionToDelimitedStringWithLimit(strings, delimiter, prefix, suffix, truncatedLength, stringBuilder); final String truncatedDescription = stringBuilder.toString(); assertThat(truncatedDescription, allOf(containsString("... (" + count + " in total,"), endsWith(" omitted)"))); @@ -237,7 +284,7 @@ public void testCollectionToDelimitedStringWithLimitNoTruncation() { strings.add(randomAlphaOfLength(between(0, 10))); } - final String fullDescription = Strings.collectionToDelimitedString(strings, delimiter, prefix, suffix); + final String fullDescription = collectionToDelimitedString(strings, delimiter, prefix, suffix); for (String string : strings) { assertThat(fullDescription, containsString(prefix + string + suffix)); } @@ -247,15 +294,40 @@ public void testCollectionToDelimitedStringWithLimitNoTruncation() { final int limit = randomFrom(between(minLimit, fullDescription.length()), between(minLimit, Integer.MAX_VALUE), Integer.MAX_VALUE); final StringBuilder stringBuilder = new StringBuilder(); - Strings.collectionToDelimitedStringWithLimit(strings, delimiter, prefix, suffix, limit, stringBuilder); + collectionToDelimitedStringWithLimit(strings, delimiter, prefix, suffix, limit, stringBuilder); assertThat(stringBuilder.toString(), equalTo(fullDescription)); } public void testPadStart() { String testStr; - assertThat(Strings.padStart("", 5, 'a'), equalTo("aaaaa")); - assertThat(Strings.padStart(testStr = randomAlphaOfLength(6), 10, ' '), equalTo(" ".repeat(4) + testStr)); - assertThat(Strings.padStart(testStr = randomAlphaOfLength(6), 5, ' '), equalTo(testStr)); - assertThat(Strings.padStart(testStr = randomAlphaOfLength(6), 10, 'f'), equalTo("f".repeat(4) + testStr)); + assertThat(padStart("", 5, 'a'), equalTo("aaaaa")); + assertThat(padStart(testStr = randomAlphaOfLength(6), 10, ' '), equalTo(" ".repeat(4) + testStr)); + assertThat(padStart(testStr = randomAlphaOfLength(6), 5, ' '), equalTo(testStr)); + assertThat(padStart(testStr = randomAlphaOfLength(6), 10, 'f'), equalTo("f".repeat(4) + testStr)); + } + + public void testToLowercaseAscii() { + String testStr; + assertThat(toLowercaseAscii(""), equalTo("")); + assertThat(toLowercaseAscii(testStr = randomAlphaOfLength(5)), equalTo(testStr.toLowerCase())); + + // all ascii characters + testStr = IntStream.rangeClosed(0, 255).mapToObj(i -> Character.toString((char)i)).collect(Collectors.joining()); + assertThat(toLowercaseAscii(testStr), equalTo(lowercaseAsciiOnly(testStr))); + + // sling in some unicode too + assertThat(toLowercaseAscii(testStr = randomUnicodeOfCodepointLength(20)), equalTo(lowercaseAsciiOnly(testStr))); + } + + private static String lowercaseAsciiOnly(String s) { + // explicitly lowercase just ascii characters + StringBuilder sb = new StringBuilder(s); + for (int i = 0; i= 'A' && c <= 'Z') { + sb.setCharAt(i, (char)(sb.charAt(i) + 32)); + } + } + return sb.toString(); } } diff --git a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/cli/EmbeddedCli.java b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/cli/EmbeddedCli.java index c1e9df46af066..0d96f61380068 100644 --- a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/cli/EmbeddedCli.java +++ b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/cli/EmbeddedCli.java @@ -29,12 +29,14 @@ import java.io.PipedOutputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Predicate; +import java.util.stream.Collectors; import static org.apache.lucene.tests.util.LuceneTestCase.createTempDir; import static org.elasticsearch.test.ESTestCase.randomBoolean; @@ -274,7 +276,7 @@ public String command(String command) throws IOException { * while we're typing a command. */ private List expectedCommandEchos(String command) { - List commandLines = Strings.splitSmart(command, "\n", false); + List commandLines = Arrays.stream(command.split("\n")).filter(s -> s.isEmpty() == false).toList(); List result = new ArrayList<>(commandLines.size() * 2); result.add("[?1h=[?2004h[33msql> [0m" + commandLines.get(0)); // Every line gets an extra new line because, I dunno, but it looks right in the CLI From 519849542bb788dc45371be3207aa9c0beec095b Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Mon, 10 Oct 2022 13:30:38 +0100 Subject: [PATCH 05/10] Oops --- plugins/untitled/src/Main.java | 78 ---------------------------------- 1 file changed, 78 deletions(-) delete mode 100644 plugins/untitled/src/Main.java diff --git a/plugins/untitled/src/Main.java b/plugins/untitled/src/Main.java deleted file mode 100644 index 98612959471cb..0000000000000 --- a/plugins/untitled/src/Main.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import javax.crypto.Cipher; -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; -import java.nio.ByteBuffer; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.util.Arrays; -import java.util.Random; -import java.util.concurrent.TimeUnit; - -public class Main { - public static void main(String[] args) throws Exception { - rsa2048(); - } - - public static void aes256() throws Exception { - KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); - keyGenerator.init(256); - SecretKey secretKey = keyGenerator.generateKey(); - byte[] data = new byte[1024*1024*50]; - - Random r = new Random(); - r.nextBytes(data); - - Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding"); - c.init(Cipher.ENCRYPT_MODE, secretKey); - byte[] encrypt = new byte[c.getOutputSize(data.length)]; - - long ns = System.nanoTime(); - c.doFinal(ByteBuffer.wrap(data), ByteBuffer.wrap(encrypt)); - System.out.println(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - ns) + "ms"); - - c.init(Cipher.DECRYPT_MODE, secretKey); - data = new byte[c.getOutputSize(encrypt.length)]; - - ns = System.nanoTime(); - c.doFinal(ByteBuffer.wrap(encrypt), ByteBuffer.wrap(data)); - System.out.println(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - ns) + "ms"); - } - - public static void rsa2048() throws Exception { - long ns = System.nanoTime(); - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(2048); - KeyPair keypair = keyGen.genKeyPair(); - System.out.println("Generation: " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - ns) + "ms"); - PrivateKey privateKey = keypair.getPrivate(); - PublicKey publicKey = keypair.getPublic(); - - byte[] data = new byte[500]; - byte[] encrypt = new byte[500]; - - Random r = new Random(); - r.nextBytes(data); - - ns = System.nanoTime(); - Cipher c = Cipher.getInstance("RSA"); - - int w=0; - //for (int b=0; b<1024*1024*5; b += 200) { - c.init(Cipher.ENCRYPT_MODE, publicKey); - w += c.doFinal(data, 0, 200, encrypt, w); - //} - System.out.println("Encryption: " + (System.nanoTime() - ns) + "ns"); - - } -} From 90ff7b2c1b762de32ab0697c62e4096ef2fe8403 Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Mon, 10 Oct 2022 13:40:58 +0100 Subject: [PATCH 06/10] more tests --- .../elasticsearch/common/StringsTests.java | 35 +++++++++++++------ .../xpack/sql/qa/cli/EmbeddedCli.java | 2 -- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/common/StringsTests.java b/server/src/test/java/org/elasticsearch/common/StringsTests.java index 36fa227a531c8..2716c729553c1 100644 --- a/server/src/test/java/org/elasticsearch/common/StringsTests.java +++ b/server/src/test/java/org/elasticsearch/common/StringsTests.java @@ -20,6 +20,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import static org.elasticsearch.common.Strings.*; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.contains; @@ -30,7 +31,6 @@ import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThanOrEqualTo; -import static org.elasticsearch.common.Strings.*; public class StringsTests extends ESTestCase { @@ -55,13 +55,23 @@ public void testSplitSmart() { } public void testHasLength() { - assertFalse(hasLength((String)null)); + assertFalse(hasLength((String) null)); assertFalse(hasLength("")); assertTrue(hasLength(" ")); assertTrue(hasLength("Hello")); + assertTrue(hasLength("\0")); } + public void testIsEmpty() { + assertTrue(isEmpty(null)); + assertTrue(isEmpty("")); + assertFalse(isEmpty(" ")); + assertFalse(isEmpty("Hello")); + + assertFalse(isEmpty("\0")); + } + public void testHasText() { assertFalse(hasText(null)); assertFalse(hasText("")); @@ -69,7 +79,10 @@ public void testHasText() { assertTrue(hasText("12345")); assertTrue(hasText(" 12345 ")); - String asciiWhitespace = IntStream.rangeClosed(0, 32).filter(Character::isWhitespace).mapToObj(Character::toString).collect(Collectors.joining()); + String asciiWhitespace = IntStream.rangeClosed(0, 32) + .filter(Character::isWhitespace) + .mapToObj(Character::toString) + .collect(Collectors.joining()); assertFalse(hasText(asciiWhitespace)); assertTrue(hasText("\ud855\udddd")); } @@ -161,17 +174,17 @@ public void testToStringToXContentWithOrWithoutParams() { } public void testDeleteAny() { - assertNull(deleteAny((CharSequence)null, "abc")); - assertNull(deleteAny((String)null, "abc")); + assertNull(deleteAny((CharSequence) null, "abc")); + assertNull(deleteAny((String) null, "abc")); assertThat(deleteAny(new StringBuilder("foo"), null), hasToString("foo")); assertThat(deleteAny("foo", null), equalTo("foo")); assertThat(deleteAny("abc\ndef\t", "az\n"), equalTo("bcdef\t")); String testStr = randomUnicodeOfLength(10); - String delete = testStr.substring(testStr.length()-1) + testStr.substring(0, 1); - assertThat(deleteAny(testStr, delete), equalTo(testStr.substring(1, testStr.length()-1))); - assertThat(deleteAny(new StringBuilder(testStr), delete), hasToString(testStr.substring(1, testStr.length()-1))); + String delete = testStr.substring(testStr.length() - 1) + testStr.substring(0, 1); + assertThat(deleteAny(testStr, delete), equalTo(testStr.substring(1, testStr.length() - 1))); + assertThat(deleteAny(new StringBuilder(testStr), delete), hasToString(testStr.substring(1, testStr.length() - 1))); // this method doesn't really work with surrogates } @@ -312,7 +325,7 @@ public void testToLowercaseAscii() { assertThat(toLowercaseAscii(testStr = randomAlphaOfLength(5)), equalTo(testStr.toLowerCase())); // all ascii characters - testStr = IntStream.rangeClosed(0, 255).mapToObj(i -> Character.toString((char)i)).collect(Collectors.joining()); + testStr = IntStream.rangeClosed(0, 255).mapToObj(i -> Character.toString((char) i)).collect(Collectors.joining()); assertThat(toLowercaseAscii(testStr), equalTo(lowercaseAsciiOnly(testStr))); // sling in some unicode too @@ -322,10 +335,10 @@ public void testToLowercaseAscii() { private static String lowercaseAsciiOnly(String s) { // explicitly lowercase just ascii characters StringBuilder sb = new StringBuilder(s); - for (int i = 0; i= 'A' && c <= 'Z') { - sb.setCharAt(i, (char)(sb.charAt(i) + 32)); + sb.setCharAt(i, (char) (sb.charAt(i) + 32)); } } return sb.toString(); diff --git a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/cli/EmbeddedCli.java b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/cli/EmbeddedCli.java index 0d96f61380068..6a1477cf05d3c 100644 --- a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/cli/EmbeddedCli.java +++ b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/cli/EmbeddedCli.java @@ -11,7 +11,6 @@ import org.elasticsearch.cli.MockTerminal; import org.elasticsearch.cli.ProcessInfo; import org.elasticsearch.cli.Terminal; -import org.elasticsearch.common.Strings; import org.elasticsearch.core.IOUtils; import org.elasticsearch.core.Nullable; import org.elasticsearch.xpack.sql.cli.Cli; @@ -36,7 +35,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Predicate; -import java.util.stream.Collectors; import static org.apache.lucene.tests.util.LuceneTestCase.createTempDir; import static org.elasticsearch.test.ESTestCase.randomBoolean; From 4d5eacb063bfcb89c5221b1699f42f9b5bde436e Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Mon, 10 Oct 2022 14:16:27 +0100 Subject: [PATCH 07/10] lowercase locale --- .../src/test/java/org/elasticsearch/common/StringsTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/test/java/org/elasticsearch/common/StringsTests.java b/server/src/test/java/org/elasticsearch/common/StringsTests.java index 2716c729553c1..c7757bd09113e 100644 --- a/server/src/test/java/org/elasticsearch/common/StringsTests.java +++ b/server/src/test/java/org/elasticsearch/common/StringsTests.java @@ -17,6 +17,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -322,7 +323,7 @@ public void testPadStart() { public void testToLowercaseAscii() { String testStr; assertThat(toLowercaseAscii(""), equalTo("")); - assertThat(toLowercaseAscii(testStr = randomAlphaOfLength(5)), equalTo(testStr.toLowerCase())); + assertThat(toLowercaseAscii(testStr = randomAlphaOfLength(5)), equalTo(testStr.toLowerCase(Locale.ROOT))); // all ascii characters testStr = IntStream.rangeClosed(0, 255).mapToObj(i -> Character.toString((char) i)).collect(Collectors.joining()); From ed0fde781703879fa1f573d3b68cbefeca6d3336 Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Mon, 10 Oct 2022 14:36:01 +0100 Subject: [PATCH 08/10] No star imports... --- .../org/elasticsearch/common/StringsTests.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/server/src/test/java/org/elasticsearch/common/StringsTests.java b/server/src/test/java/org/elasticsearch/common/StringsTests.java index c7757bd09113e..a11ecf805d001 100644 --- a/server/src/test/java/org/elasticsearch/common/StringsTests.java +++ b/server/src/test/java/org/elasticsearch/common/StringsTests.java @@ -21,7 +21,22 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import static org.elasticsearch.common.Strings.*; +import static org.elasticsearch.common.Strings.cleanTruncate; +import static org.elasticsearch.common.Strings.collectionToDelimitedString; +import static org.elasticsearch.common.Strings.collectionToDelimitedStringWithLimit; +import static org.elasticsearch.common.Strings.deleteAny; +import static org.elasticsearch.common.Strings.delimitedListToStringArray; +import static org.elasticsearch.common.Strings.hasLength; +import static org.elasticsearch.common.Strings.hasText; +import static org.elasticsearch.common.Strings.isAllOrWildcard; +import static org.elasticsearch.common.Strings.isEmpty; +import static org.elasticsearch.common.Strings.padStart; +import static org.elasticsearch.common.Strings.spaceify; +import static org.elasticsearch.common.Strings.splitSmart; +import static org.elasticsearch.common.Strings.substring; +import static org.elasticsearch.common.Strings.toLowercaseAscii; +import static org.elasticsearch.common.Strings.tokenizeByCommaToSet; +import static org.elasticsearch.common.Strings.trimLeadingCharacter; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.contains; From 6a374d4d53988b49638f7e52a67d496b636574cf Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Tue, 11 Oct 2022 15:55:53 +0100 Subject: [PATCH 09/10] Remove methods --- .../src/main/java/org/elasticsearch/common/Strings.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/Strings.java b/server/src/main/java/org/elasticsearch/common/Strings.java index 57a916d5b3e88..bf5f8d02797c0 100644 --- a/server/src/main/java/org/elasticsearch/common/Strings.java +++ b/server/src/main/java/org/elasticsearch/common/Strings.java @@ -942,14 +942,6 @@ public static boolean isNullOrBlank(@Nullable String s) { return s == null || s.isBlank(); } - /** - * @deprecated Use RequireNonNullElse instead - */ - @Deprecated - public static String coalesceToEmpty(@Nullable String s) { - return Objects.requireNonNullElse(s, ""); - } - public static String padStart(String s, int minimumLength, char c) { Objects.requireNonNull(s, "s"); if (s.length() >= minimumLength) { From 6c45314f615db88db814ec12775a41e550031d0c Mon Sep 17 00:00:00 2001 From: Simon Cooper Date: Thu, 13 Oct 2022 15:19:37 +0100 Subject: [PATCH 10/10] Delete unused splitSmart method --- .../org/elasticsearch/common/Strings.java | 57 ------------------- .../elasticsearch/common/StringsTests.java | 9 --- 2 files changed, 66 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/Strings.java b/server/src/main/java/org/elasticsearch/common/Strings.java index bf5f8d02797c0..5dc461de2ed97 100644 --- a/server/src/main/java/org/elasticsearch/common/Strings.java +++ b/server/src/main/java/org/elasticsearch/common/Strings.java @@ -53,63 +53,6 @@ public static void spaceify(int spaces, String from, StringBuilder to) throws IO } } - /** - * Splits a backslash escaped string on the separator. - *

- * Current backslash escaping supported: - *
\n \t \r \b \f are escaped the same as a Java String - *
Other characters following a backslash are produced verbatim (\c => c) - * - * @param s the string to split - * @param separator the separator to split on - * @param decode decode backslash escaping - */ - @Deprecated // this method is too complicated for its own good - public static List splitSmart(String s, String separator, boolean decode) { - ArrayList lst = new ArrayList<>(2); - StringBuilder sb = new StringBuilder(); - int pos = 0, end = s.length(); - while (pos < end) { - if (separator.isEmpty() == false && s.startsWith(separator, pos)) { - if (sb.length() > 0) { - lst.add(sb.toString()); - sb.setLength(0); - } - pos += separator.length(); - continue; - } - - char ch = s.charAt(pos++); - if (ch == '\\') { - if (decode == false) { - sb.append(ch); - } - if (pos >= end) { - break; // ERROR, or let it go? - } - ch = s.charAt(pos++); - if (decode) { - ch = switch (ch) { - case 'n' -> '\n'; - case 't' -> '\t'; - case 'r' -> '\r'; - case 'b' -> '\b'; - case 'f' -> '\f'; - default -> ch; - }; - } - } - - sb.append(ch); - } - - if (sb.length() > 0) { - lst.add(sb.toString()); - } - - return lst; - } - // --------------------------------------------------------------------- // General convenience methods for working with Strings // --------------------------------------------------------------------- diff --git a/server/src/test/java/org/elasticsearch/common/StringsTests.java b/server/src/test/java/org/elasticsearch/common/StringsTests.java index a11ecf805d001..8e25ee55652c5 100644 --- a/server/src/test/java/org/elasticsearch/common/StringsTests.java +++ b/server/src/test/java/org/elasticsearch/common/StringsTests.java @@ -32,14 +32,12 @@ import static org.elasticsearch.common.Strings.isEmpty; import static org.elasticsearch.common.Strings.padStart; import static org.elasticsearch.common.Strings.spaceify; -import static org.elasticsearch.common.Strings.splitSmart; import static org.elasticsearch.common.Strings.substring; import static org.elasticsearch.common.Strings.toLowercaseAscii; import static org.elasticsearch.common.Strings.tokenizeByCommaToSet; import static org.elasticsearch.common.Strings.trimLeadingCharacter; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.arrayContaining; -import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyArray; import static org.hamcrest.Matchers.endsWith; @@ -63,13 +61,6 @@ public void testSpaceify() throws Exception { assertThat(sb.toString(), equalTo(Arrays.stream(lines).collect(Collectors.joining("\n", "", "\n")))); } - public void testSplitSmart() { - String testStr; - assertThat(splitSmart("foobar", "", false), contains("foobar")); - assertThat(splitSmart(testStr = "a\nb\r\nc\td\be\f", "", false), contains(testStr)); - assertThat(splitSmart(testStr = "a\nb\r\nc\td\be\f", "", true), contains(testStr)); - } - public void testHasLength() { assertFalse(hasLength((String) null)); assertFalse(hasLength(""));