diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/text/Text.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/text/Text.java index 7a6eb5d184..8b694cd656 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/text/Text.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/text/Text.java @@ -193,19 +193,36 @@ public final class Text implements TruffleObject { boolean allowSideEffects, @Cached("build()") @Cached.Shared("strings") ToJavaStringNode toJavaStringNode) { String str = toJavaStringNode.execute(this); - // TODO This should be more extensible - String replaced = - str.replace("\\", "\\\\") - .replace("'", "\\'") - .replace("\n", "\\n") - .replace("\t", "\\t") - .replace("\u0007", "\\a") - .replace("\u0008", "\\b") - .replace("\u000c", "\\f") - .replace("\r", "\\r") - .replace("\u000B", "\\v") - .replace("\u001B", "\\e"); - return "'" + replaced + "'"; + int len = str.length(); + int outputLength = len + 2; // Precise if there are no special characters. + + // TODO This should be more extensible; while it's still a small fixed set, + // a switch is probably fastest (unconfirmed) + + StringBuffer strBuf = new StringBuffer(outputLength); + + strBuf.append('\''); + + for (int i = 0; i < len; ++i) { + char c = str.charAt(i); + switch (c) { + case '\\' -> strBuf.append("\\\\"); + case '\'' -> strBuf.append("\\'"); + case '\n' -> strBuf.append("\\n"); + case '\t' -> strBuf.append("\\t"); + case '\u0007' -> strBuf.append("\\a"); + case '\u0008' -> strBuf.append("\\b"); + case '\u000c' -> strBuf.append("\\f"); + case '\r' -> strBuf.append("\\r"); + case '\u000B' -> strBuf.append("\\v"); + case '\u001B' -> strBuf.append("\\e"); + default -> strBuf.append(c); + } + } + + strBuf.append('\''); + + return strBuf.toString(); } @ExportMessage diff --git a/test/Benchmarks/src/Text/Pretty.enso b/test/Benchmarks/src/Text/Pretty.enso new file mode 100644 index 0000000000..7857fc03c2 --- /dev/null +++ b/test/Benchmarks/src/Text/Pretty.enso @@ -0,0 +1,37 @@ +from Standard.Base import all + +from Standard.Test import Bench, Faker + +# Benchmarks ################################################################## + +cycle_character_template num character_templates = + character_templates.at (num % (character_templates.length)) + +bench = + ## The `Text.pretty` benchmarks check both scenarios where the Texts are + short and very long. This is to verify that making a single pass over + the text is in fact faster than multiple passes. + bench_pretty suite_prefix = + faker = Faker.new + + regular_characters = (Faker.upper_case_letters + Faker.lower_case_letters) + # Keep this up to date with the special characters in Text.java + special_characters = '\\\'\n\t\u{7}\u{8}\u{c}\r\u{b}\u{1b}'.char_vector + + # 20% special, 80% regular + make_template length = + r = regular_characters + s = special_characters + Vector.new length ix-> cycle_character_template ix [r, r, s, s, r] + + very_short_template = make_template 4 + very_short_random = faker.string_value very_short_template + big_template = make_template 100000 + big_random = faker.string_value big_template + + Bench.measure (very_short_random.pretty) suite_prefix+" 4" 10 10 + Bench.measure (big_random.pretty) suite_prefix+" 100000" 10 10 + + bench_pretty "Text.pretty" + +main = bench