From 16d68f85636202cda5add65e51239761d91c1bd6 Mon Sep 17 00:00:00 2001 From: Bao-Long Nguyen-Trong Date: Mon, 3 Nov 2025 15:38:03 -0800 Subject: [PATCH 1/6] Lazy init valix hex array Init of a 64k boolean array take a long time (>30ms P90) during Android app launch. --- .../api/internal/OtelEncodingUtils.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java b/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java index bb6a5484488..808e59df2b2 100644 --- a/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java +++ b/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java @@ -21,7 +21,8 @@ public final class OtelEncodingUtils { private static final int NUM_ASCII_CHARACTERS = 128; private static final char[] ENCODING = buildEncodingArray(); private static final byte[] DECODING = buildDecodingArray(); - private static final boolean[] VALID_HEX = buildValidHexArray(); + + private static volatile boolean[] validHex = null; private static char[] buildEncodingArray() { char[] encoding = new char[512]; @@ -152,7 +153,15 @@ public static boolean isValidBase16String(CharSequence value) { /** Returns whether the given {@code char} is a valid hex character. */ public static boolean isValidBase16Character(char b) { - return VALID_HEX[b]; + if (validHex == null) { + synchronized (OtelEncodingUtils.class) { + if (validHex == null) { + validHex = buildValidHexArray(); + } + } + } + + return validHex[b]; } private OtelEncodingUtils() {} From 652457b65c6229b920a3276ae0c660d769cd1faa Mon Sep 17 00:00:00 2001 From: Bao-Long Nguyen-Trong Date: Mon, 3 Nov 2025 15:44:13 -0800 Subject: [PATCH 2/6] Add @Nullable --- .../java/io/opentelemetry/api/internal/OtelEncodingUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java b/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java index 808e59df2b2..cfdcdcb514d 100644 --- a/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java +++ b/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java @@ -6,6 +6,7 @@ package io.opentelemetry.api.internal; import java.util.Arrays; +import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; /** @@ -22,7 +23,7 @@ public final class OtelEncodingUtils { private static final char[] ENCODING = buildEncodingArray(); private static final byte[] DECODING = buildDecodingArray(); - private static volatile boolean[] validHex = null; + @Nullable private static volatile boolean[] validHex = null; private static char[] buildEncodingArray() { char[] encoding = new char[512]; From 474b0e44694bcb98372b734a7372ab147ebb714e Mon Sep 17 00:00:00 2001 From: Bao-Long Nguyen-Trong Date: Tue, 4 Nov 2025 11:06:02 -0800 Subject: [PATCH 3/6] Change to lazy init values only Instead of lazy init the whole array, we lazy init each values. --- .../api/internal/OtelEncodingUtils.java | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java b/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java index cfdcdcb514d..fc70ef8dd68 100644 --- a/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java +++ b/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java @@ -6,7 +6,6 @@ package io.opentelemetry.api.internal; import java.util.Arrays; -import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; /** @@ -22,8 +21,16 @@ public final class OtelEncodingUtils { private static final int NUM_ASCII_CHARACTERS = 128; private static final char[] ENCODING = buildEncodingArray(); private static final byte[] DECODING = buildDecodingArray(); - - @Nullable private static volatile boolean[] validHex = null; + /** + * Stores whether the character at that index is a valid HEX character. + * We lazy init the array values to minimize impact during process startup esp. on mobile. + * A value of 0 means the character validity has not been determined yet. + * A value of 1 means the character is a valid HEX character. + * A value of -1 means the character is an invalid HEX character. + * + * @see #isValidBase16Character(char) + */ + private static final int[] VALID_HEX = new int[Character.MAX_VALUE]; private static char[] buildEncodingArray() { char[] encoding = new char[512]; @@ -44,14 +51,6 @@ private static byte[] buildDecodingArray() { return decoding; } - private static boolean[] buildValidHexArray() { - boolean[] validHex = new boolean[Character.MAX_VALUE]; - for (int i = 0; i < Character.MAX_VALUE; i++) { - validHex[i] = (48 <= i && i <= 57) || (97 <= i && i <= 102); - } - return validHex; - } - /** * Returns the {@code long} value whose base16 representation is stored in the first 16 chars of * {@code chars} starting from the {@code offset}. @@ -154,15 +153,12 @@ public static boolean isValidBase16String(CharSequence value) { /** Returns whether the given {@code char} is a valid hex character. */ public static boolean isValidBase16Character(char b) { - if (validHex == null) { - synchronized (OtelEncodingUtils.class) { - if (validHex == null) { - validHex = buildValidHexArray(); - } - } + int isValid = VALID_HEX[b]; + if (isValid == 0) { + isValid = (48 <= b && b <= 57) || (97 <= b && b <= 102) ? 1 : -1; + VALID_HEX[b] = isValid; } - - return validHex[b]; + return isValid == 1; } private OtelEncodingUtils() {} From 5e93095dc7d192a24e3922f733e83f932b4028c9 Mon Sep 17 00:00:00 2001 From: Bao-Long Nguyen-Trong Date: Fri, 12 Dec 2025 18:35:44 -0800 Subject: [PATCH 4/6] Apply Spotless --- .../api/internal/OtelEncodingUtils.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java b/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java index fc70ef8dd68..d19a1a65632 100644 --- a/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java +++ b/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java @@ -21,14 +21,14 @@ public final class OtelEncodingUtils { private static final int NUM_ASCII_CHARACTERS = 128; private static final char[] ENCODING = buildEncodingArray(); private static final byte[] DECODING = buildDecodingArray(); + /** - * Stores whether the character at that index is a valid HEX character. - * We lazy init the array values to minimize impact during process startup esp. on mobile. - * A value of 0 means the character validity has not been determined yet. - * A value of 1 means the character is a valid HEX character. - * A value of -1 means the character is an invalid HEX character. - * - * @see #isValidBase16Character(char) + * Stores whether the character at that index is a valid HEX character. We lazy init the array + * values to minimize impact during process startup esp. on mobile. A value of 0 means the + * character validity has not been determined yet. A value of 1 means the character is a valid HEX + * character. A value of -1 means the character is an invalid HEX character. + * + * @see #isValidBase16Character(char) */ private static final int[] VALID_HEX = new int[Character.MAX_VALUE]; From 2770b7cc1b28321a59282f83ed10f973303f307a Mon Sep 17 00:00:00 2001 From: Bao-Long Nguyen-Trong Date: Fri, 12 Dec 2025 23:19:55 -0800 Subject: [PATCH 5/6] Switch to only init between the extreme HEX values (vs all 64K) --- .../api/internal/OtelEncodingUtils.java | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java b/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java index d19a1a65632..19fa277b377 100644 --- a/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java +++ b/api/all/src/main/java/io/opentelemetry/api/internal/OtelEncodingUtils.java @@ -21,16 +21,7 @@ public final class OtelEncodingUtils { private static final int NUM_ASCII_CHARACTERS = 128; private static final char[] ENCODING = buildEncodingArray(); private static final byte[] DECODING = buildDecodingArray(); - - /** - * Stores whether the character at that index is a valid HEX character. We lazy init the array - * values to minimize impact during process startup esp. on mobile. A value of 0 means the - * character validity has not been determined yet. A value of 1 means the character is a valid HEX - * character. A value of -1 means the character is an invalid HEX character. - * - * @see #isValidBase16Character(char) - */ - private static final int[] VALID_HEX = new int[Character.MAX_VALUE]; + private static final boolean[] VALID_HEX = buildValidHexArray(); private static char[] buildEncodingArray() { char[] encoding = new char[512]; @@ -51,6 +42,17 @@ private static byte[] buildDecodingArray() { return decoding; } + private static boolean[] buildValidHexArray() { + boolean[] validHex = new boolean[Character.MAX_VALUE]; + // Use the fact that the default initial boolean value is false + // and only explicitly init the values between the extreme HEX values + // This minimized impact during process startup esp. on mobile. + for (int i = 48; i < 103; i++) { + validHex[i] = i <= 57 || 97 <= i; + } + return validHex; + } + /** * Returns the {@code long} value whose base16 representation is stored in the first 16 chars of * {@code chars} starting from the {@code offset}. @@ -153,12 +155,7 @@ public static boolean isValidBase16String(CharSequence value) { /** Returns whether the given {@code char} is a valid hex character. */ public static boolean isValidBase16Character(char b) { - int isValid = VALID_HEX[b]; - if (isValid == 0) { - isValid = (48 <= b && b <= 57) || (97 <= b && b <= 102) ? 1 : -1; - VALID_HEX[b] = isValid; - } - return isValid == 1; + return VALID_HEX[b]; } private OtelEncodingUtils() {} From 4e6d27424602d4816e1409389671f89676cb27ec Mon Sep 17 00:00:00 2001 From: Bao-Long Nguyen-Trong Date: Sat, 13 Dec 2025 08:07:11 -0800 Subject: [PATCH 6/6] Trigger Build