From 53ef169850c0c016437bc0374d1c98f189a6036a Mon Sep 17 00:00:00 2001 From: OmarAlJarrah Date: Sun, 28 Jun 2026 03:13:25 +0300 Subject: [PATCH 1/2] chore: dedupe TeeSink typed writes and tidy logging/redaction helpers Trim a few self-contained duplications in the io and instrumentation packages. All changes are behavior-preserving and touch only internal or private members. - TeeSink: replace the hand-rolled if/else min in tapAllowance with the standard minOf clamp, and fold the five typed-write overrides (write/writeUtf8/writeString) onto a single private inline `staged` helper that stages into the scratch buffer, tees, and drains. The helper takes the staging buffer as an explicit lambda argument rather than a Buffer receiver, so an unqualified write can never resolve back to the TeeSink itself and self-recurse. - ClientLogger: drop the pass-through toSlf4j and inline its level mapping into slf4jLevel; point canLog at slf4jLevel directly. Removes one redundant forwarding hop; canLog's signature is unchanged. - UrlRedactor: collapse appendRedactedPair's three-local conditional block to a single eq < 0 early return for the bare-name case, so the value substring and allow-list decode are computed only when a value is actually present. Output is byte-for-byte identical. Closes #178 --- .../sdk/core/instrumentation/ClientLogger.kt | 20 ++++---- .../sdk/core/instrumentation/UrlRedactor.kt | 28 ++++------- .../kotlin/org/dexpace/sdk/core/io/TeeSink.kt | 47 ++++++++----------- 3 files changed, 38 insertions(+), 57 deletions(-) diff --git a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/instrumentation/ClientLogger.kt b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/instrumentation/ClientLogger.kt index 57987a59..785743d3 100644 --- a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/instrumentation/ClientLogger.kt +++ b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/instrumentation/ClientLogger.kt @@ -141,9 +141,16 @@ public class ClientLogger private constructor( * Equivalent to SLF4J `Logger.isEnabledForLevel(Level)`; named `canLog` to read naturally * at call sites such as `if (logger.canLog(LogLevel.VERBOSE)) { … }`. */ - public fun canLog(level: LogLevel): Boolean = slf4j.isEnabledForLevel(toSlf4j(level)) + public fun canLog(level: LogLevel): Boolean = slf4j.isEnabledForLevel(slf4jLevel(level)) - internal fun slf4jLevel(level: LogLevel): Level = toSlf4j(level) + internal fun slf4jLevel(level: LogLevel): Level = + when (level) { + LogLevel.ERROR -> Level.ERROR + LogLevel.WARNING -> Level.WARN + LogLevel.INFO -> Level.INFO + // SLF4J has no VERBOSE; map to DEBUG (the closest convention). + LogLevel.VERBOSE -> Level.DEBUG + } /** * One-shot guard for the [warnDroppedEventFieldOnce] diagnostic. The misuse it flags — a @@ -175,15 +182,6 @@ public class ClientLogger private constructor( eventNameTag, ) } - - private fun toSlf4j(level: LogLevel): Level = - when (level) { - LogLevel.ERROR -> Level.ERROR - LogLevel.WARNING -> Level.WARN - LogLevel.INFO -> Level.INFO - // SLF4J has no VERBOSE; map to DEBUG (the closest convention). - LogLevel.VERBOSE -> Level.DEBUG - } } /** diff --git a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/instrumentation/UrlRedactor.kt b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/instrumentation/UrlRedactor.kt index bf20619c..04957f2a 100644 --- a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/instrumentation/UrlRedactor.kt +++ b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/instrumentation/UrlRedactor.kt @@ -169,29 +169,19 @@ public object UrlRedactor { ) { if (pair.isEmpty()) return val eq = pair.indexOf('=') - val encodedName: String - val hasValue: Boolean - val encodedValue: String if (eq < 0) { - encodedName = pair - hasValue = false - encodedValue = "" - } else { - encodedName = pair.substring(0, eq) - hasValue = true - encodedValue = pair.substring(eq + 1) + // Bare name with no '=' — emit verbatim; there is no value to redact. + out.append(pair) + return } - val allowed = allowedLower.contains(safeDecode(encodedName).lowercase()) - + val encodedName = pair.substring(0, eq) out.append(encodedName) - if (hasValue) { - out.append('=') - if (allowed) { - out.append(encodedValue) - } else { - out.append(REDACTED_ENCODED) - } + out.append('=') + if (allowedLower.contains(safeDecode(encodedName).lowercase())) { + out.append(pair.substring(eq + 1)) + } else { + out.append(REDACTED_ENCODED) } } diff --git a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/io/TeeSink.kt b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/io/TeeSink.kt index 0af6238c..4cbc214b 100644 --- a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/io/TeeSink.kt +++ b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/io/TeeSink.kt @@ -104,10 +104,8 @@ internal class TeeSink( * [requested] and the remaining [tapLimit] budget, clamped to never go negative. The actual * copy and [mirrored] advancement stay at each call site. */ - private fun tapAllowance(requested: Long): Long { - val remaining = (tapLimit - mirrored).coerceAtLeast(0L) - return if (requested < remaining) requested else remaining - } + private fun tapAllowance(requested: Long): Long = + minOf(requested, (tapLimit - mirrored).coerceAtLeast(0L)) @Throws(IOException::class) override fun flush() { @@ -119,23 +117,29 @@ internal class TeeSink( primary.close() } - @Throws(IOException::class) - override fun write(source: ByteArray): BufferedSink { - scratch.write(source) + /** + * Stages one typed write into [scratch] then tees it into the tap and drains it into the + * primary in a single pass, returning `this` for chaining. [encode] receives [scratch] + * explicitly as its `it` argument so the staged write always targets the staging [Buffer] — + * never one of this `TeeSink`'s own `write*` overrides, which a `Buffer.()` receiver lambda + * could silently rebind to (and self-recurse) if a `Buffer` overload were ever added. + */ + private inline fun staged(encode: (Buffer) -> Unit): BufferedSink { + encode(scratch) drainScratch() return this } + @Throws(IOException::class) + override fun write(source: ByteArray): BufferedSink = + staged { it.write(source) } + @Throws(IOException::class) override fun write( source: ByteArray, offset: Int, byteCount: Int, - ): BufferedSink { - scratch.write(source, offset, byteCount) - drainScratch() - return this - } + ): BufferedSink = staged { it.write(source, offset, byteCount) } @Throws(IOException::class) override fun writeAll(source: Source): Long { @@ -157,32 +161,21 @@ internal class TeeSink( } @Throws(IOException::class) - override fun writeUtf8(string: String): BufferedSink { - scratch.writeUtf8(string) - drainScratch() - return this - } + override fun writeUtf8(string: String): BufferedSink = + staged { it.writeUtf8(string) } @Throws(IOException::class) override fun writeUtf8( string: String, beginIndex: Int, endIndex: Int, - ): BufferedSink { - scratch.writeUtf8(string, beginIndex, endIndex) - drainScratch() - return this - } + ): BufferedSink = staged { it.writeUtf8(string, beginIndex, endIndex) } @Throws(IOException::class) override fun writeString( string: String, charset: Charset, - ): BufferedSink { - scratch.writeString(string, charset) - drainScratch() - return this - } + ): BufferedSink = staged { it.writeString(string, charset) } @Throws(IOException::class) override fun outputStream(): OutputStream { From c7853dbe981744bfe7c2b7858bd73ad5809f0161 Mon Sep 17 00:00:00 2001 From: OmarAlJarrah Date: Sun, 28 Jun 2026 03:19:58 +0300 Subject: [PATCH 2/2] chore: inline short single-expression bodies in TeeSink for ktlint --- .../src/main/kotlin/org/dexpace/sdk/core/io/TeeSink.kt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/io/TeeSink.kt b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/io/TeeSink.kt index 4cbc214b..fb8bac44 100644 --- a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/io/TeeSink.kt +++ b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/io/TeeSink.kt @@ -104,8 +104,7 @@ internal class TeeSink( * [requested] and the remaining [tapLimit] budget, clamped to never go negative. The actual * copy and [mirrored] advancement stay at each call site. */ - private fun tapAllowance(requested: Long): Long = - minOf(requested, (tapLimit - mirrored).coerceAtLeast(0L)) + private fun tapAllowance(requested: Long): Long = minOf(requested, (tapLimit - mirrored).coerceAtLeast(0L)) @Throws(IOException::class) override fun flush() { @@ -131,8 +130,7 @@ internal class TeeSink( } @Throws(IOException::class) - override fun write(source: ByteArray): BufferedSink = - staged { it.write(source) } + override fun write(source: ByteArray): BufferedSink = staged { it.write(source) } @Throws(IOException::class) override fun write( @@ -161,8 +159,7 @@ internal class TeeSink( } @Throws(IOException::class) - override fun writeUtf8(string: String): BufferedSink = - staged { it.writeUtf8(string) } + override fun writeUtf8(string: String): BufferedSink = staged { it.writeUtf8(string) } @Throws(IOException::class) override fun writeUtf8(