diff --git a/library/base16/README.md b/library/base16/README.md index 2b323aae..fb79e479 100644 --- a/library/base16/README.md +++ b/library/base16/README.md @@ -6,7 +6,9 @@ Base16 (a.k.a. "hex") encoding/decoding in accordance with [RFC 4648 section 8][ val base16 = Base16.Builder { isLenient(enable = true) lineBreak(interval = 64) + lineBreakReset(onFlush = true) encodeLowercase(enable = true) + backFillBuffers(enable = true) } val text = "Hello World!" diff --git a/library/base16/api/base16.api b/library/base16/api/base16.api index 330f0f5a..39b341ac 100644 --- a/library/base16/api/base16.api +++ b/library/base16/api/base16.api @@ -24,6 +24,7 @@ public final class io/matthewnelson/encoding/base16/Base16$Builder { public final fun encodeLowercase (Z)Lio/matthewnelson/encoding/base16/Base16$Builder; public final fun isLenient (Z)Lio/matthewnelson/encoding/base16/Base16$Builder; public final fun lineBreak (B)Lio/matthewnelson/encoding/base16/Base16$Builder; + public final fun lineBreakReset (Z)Lio/matthewnelson/encoding/base16/Base16$Builder; public final fun strictSpec ()Lio/matthewnelson/encoding/base16/Base16$Builder; } @@ -36,7 +37,7 @@ public final class io/matthewnelson/encoding/base16/Base16$Config : io/matthewne public final field encodeLowercase Z public final field encodeToLowercase Z public final field isConstantTime Z - public synthetic fun (ZBZZLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (ZBZZZLkotlin/jvm/internal/DefaultConstructorMarker;)V } public final class io/matthewnelson/encoding/base16/Base16ConfigBuilder { diff --git a/library/base16/api/base16.klib.api b/library/base16/api/base16.klib.api index 70fe4695..3bd57f4f 100644 --- a/library/base16/api/base16.klib.api +++ b/library/base16/api/base16.klib.api @@ -18,6 +18,7 @@ final class io.matthewnelson.encoding.base16/Base16 : io.matthewnelson.encoding. final fun encodeLowercase(kotlin/Boolean): io.matthewnelson.encoding.base16/Base16.Builder // io.matthewnelson.encoding.base16/Base16.Builder.encodeLowercase|encodeLowercase(kotlin.Boolean){}[0] final fun isLenient(kotlin/Boolean): io.matthewnelson.encoding.base16/Base16.Builder // io.matthewnelson.encoding.base16/Base16.Builder.isLenient|isLenient(kotlin.Boolean){}[0] final fun lineBreak(kotlin/Byte): io.matthewnelson.encoding.base16/Base16.Builder // io.matthewnelson.encoding.base16/Base16.Builder.lineBreak|lineBreak(kotlin.Byte){}[0] + final fun lineBreakReset(kotlin/Boolean): io.matthewnelson.encoding.base16/Base16.Builder // io.matthewnelson.encoding.base16/Base16.Builder.lineBreakReset|lineBreakReset(kotlin.Boolean){}[0] final fun strictSpec(): io.matthewnelson.encoding.base16/Base16.Builder // io.matthewnelson.encoding.base16/Base16.Builder.strictSpec|strictSpec(){}[0] } diff --git a/library/base16/src/commonMain/kotlin/io/matthewnelson/encoding/base16/Base16.kt b/library/base16/src/commonMain/kotlin/io/matthewnelson/encoding/base16/Base16.kt index 0e05e4cf..96db7b68 100644 --- a/library/base16/src/commonMain/kotlin/io/matthewnelson/encoding/base16/Base16.kt +++ b/library/base16/src/commonMain/kotlin/io/matthewnelson/encoding/base16/Base16.kt @@ -40,7 +40,9 @@ import kotlin.jvm.JvmSynthetic * val base16 = Base16.Builder { * isLenient(enable = true) * lineBreak(interval = 64) + * lineBreakReset(onFlush = true) * encodeLowercase(enable = true) + * backFillBuffers(enable = true) * } * * val text = "Hello World!" @@ -72,22 +74,26 @@ public class Base16: EncoderDecoder { if (other == null) return this._isLenient = other.isLenient ?: true this._lineBreakInterval = other.lineBreakInterval + this._lineBreakResetOnFlush = other.lineBreakResetOnFlush this._encodeLowercase = other.encodeLowercase this._backFillBuffers = other.backFillBuffers } @get:JvmSynthetic - @set:JvmSynthetic internal var _isLenient: Boolean = true + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _lineBreakInterval: Byte = 0 + private set + @get:JvmSynthetic + internal var _lineBreakResetOnFlush: Boolean = true + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _encodeLowercase: Boolean = false + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _backFillBuffers: Boolean = true + private set /** * DEFAULT: `true` @@ -130,6 +136,13 @@ public class Base16: EncoderDecoder { * */ public fun lineBreak(interval: Byte): Builder = apply { _lineBreakInterval = interval } + /** + * DEFAULT: `true` + * + * @see [EncoderDecoder.Config.lineBreakResetOnFlush] + * */ + public fun lineBreakReset(onFlush: Boolean): Builder = apply { _lineBreakResetOnFlush = onFlush } + /** * DEFAULT: `false` * @@ -180,12 +193,14 @@ public class Base16: EncoderDecoder { public class Config private constructor( isLenient: Boolean, lineBreakInterval: Byte, + lineBreakResetOnFlush: Boolean, @JvmField public val encodeLowercase: Boolean, backFillBuffers: Boolean, ): EncoderDecoder.Config( isLenient, lineBreakInterval, + lineBreakResetOnFlush, paddingChar = null, maxDecodeEmit = 1, backFillBuffers, @@ -222,6 +237,7 @@ public class Base16: EncoderDecoder { internal val DEFAULT: Config = Config( isLenient = true, lineBreakInterval = 64, + lineBreakResetOnFlush = true, encodeLowercase = false, backFillBuffers = true, ) diff --git a/library/base16/src/commonMain/kotlin/io/matthewnelson/encoding/base16/Builders.kt b/library/base16/src/commonMain/kotlin/io/matthewnelson/encoding/base16/Builders.kt index 9192d324..edcaa4d4 100644 --- a/library/base16/src/commonMain/kotlin/io/matthewnelson/encoding/base16/Builders.kt +++ b/library/base16/src/commonMain/kotlin/io/matthewnelson/encoding/base16/Builders.kt @@ -86,6 +86,13 @@ public class Base16ConfigBuilder { isLenient = compat._isLenient lineBreakInterval = compat._lineBreakInterval encodeToLowercase = compat._encodeLowercase + + // Prior to 2.6.0, behavior was broken whereby the + // LineBreakOutFeed was not being reset whenever + // Encoder.Feed.flush was called. This maintains + // that behavior for consumers who have not updated + // to the Builder API. + compat.lineBreakReset(onFlush = false) } /** diff --git a/library/base16/src/commonMain/kotlin/io/matthewnelson/encoding/base16/internal/-Config.kt b/library/base16/src/commonMain/kotlin/io/matthewnelson/encoding/base16/internal/-Config.kt index 6900d140..b434ab43 100644 --- a/library/base16/src/commonMain/kotlin/io/matthewnelson/encoding/base16/internal/-Config.kt +++ b/library/base16/src/commonMain/kotlin/io/matthewnelson/encoding/base16/internal/-Config.kt @@ -19,13 +19,14 @@ package io.matthewnelson.encoding.base16.internal import io.matthewnelson.encoding.base16.Base16 -internal inline fun ((Boolean, Byte, Boolean, Boolean) -> Base16.Config).build( +internal inline fun ((Boolean, Byte, Boolean, Boolean, Boolean) -> Base16.Config).build( b: Base16.Builder, noinline base16: (Base16.Config, Any?) -> Base16, ): Base16 { if ( b._isLenient == Base16.DELEGATE.config.isLenient && b._lineBreakInterval == Base16.DELEGATE.config.lineBreakInterval + && b._lineBreakResetOnFlush == Base16.DELEGATE.config.lineBreakResetOnFlush && b._encodeLowercase == Base16.DELEGATE.config.encodeLowercase && b._backFillBuffers == Base16.DELEGATE.config.backFillBuffers ) { @@ -34,6 +35,7 @@ internal inline fun ((Boolean, Byte, Boolean, Boolean) -> Base16.Config).build( val config = this( b._isLenient, b._lineBreakInterval, + b._lineBreakResetOnFlush, b._encodeLowercase, b._backFillBuffers, ) diff --git a/library/base32/README.md b/library/base32/README.md index 0ecd43b3..26b8b2c5 100644 --- a/library/base32/README.md +++ b/library/base32/README.md @@ -8,6 +8,7 @@ val crockford = Base32.Crockford.Builder { encodeLowercase(enable = false) hyphen(interval = 5) check(symbol = '~') + backFillBuffers(enable = true) } val text = "Hello World!" @@ -25,8 +26,10 @@ assertEquals(text, decoded) val default = Base32.Default.Builder { isLenient(enable = true) lineBreak(interval = 64) + lineBreakReset(onFlush = true) encodeLowercase(enable = false) padEncoded(enable = true) + backFillBuffers(enable = true) } val text = "Hello World!" @@ -44,8 +47,10 @@ assertEquals(text, decoded) val hex = Base32.Hex.Builder { isLenient(enable = true) lineBreak(interval = 64) + lineBreakReset(onFlush = true) encodeLowercase(enable = false) padEncoded(enable = true) + backFillBuffers(enable = true) } val text = "Hello World!" diff --git a/library/base32/api/base32.api b/library/base32/api/base32.api index 6e8d890e..2c840792 100644 --- a/library/base32/api/base32.api +++ b/library/base32/api/base32.api @@ -118,6 +118,7 @@ public final class io/matthewnelson/encoding/base32/Base32$Default$Builder { public final fun encodeLowercase (Z)Lio/matthewnelson/encoding/base32/Base32$Default$Builder; public final fun isLenient (Z)Lio/matthewnelson/encoding/base32/Base32$Default$Builder; public final fun lineBreak (B)Lio/matthewnelson/encoding/base32/Base32$Default$Builder; + public final fun lineBreakReset (Z)Lio/matthewnelson/encoding/base32/Base32$Default$Builder; public final fun padEncoded (Z)Lio/matthewnelson/encoding/base32/Base32$Default$Builder; public final fun strictSpec ()Lio/matthewnelson/encoding/base32/Base32$Default$Builder; } @@ -132,7 +133,7 @@ public final class io/matthewnelson/encoding/base32/Base32$Default$Config : io/m public final field encodeToLowercase Z public final field isConstantTime Z public final field padEncoded Z - public synthetic fun (ZBZZZLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (ZBZZZZLkotlin/jvm/internal/DefaultConstructorMarker;)V } public final class io/matthewnelson/encoding/base32/Base32$Hex : io/matthewnelson/encoding/base32/Base32 { @@ -153,6 +154,7 @@ public final class io/matthewnelson/encoding/base32/Base32$Hex$Builder { public final fun encodeLowercase (Z)Lio/matthewnelson/encoding/base32/Base32$Hex$Builder; public final fun isLenient (Z)Lio/matthewnelson/encoding/base32/Base32$Hex$Builder; public final fun lineBreak (B)Lio/matthewnelson/encoding/base32/Base32$Hex$Builder; + public final fun lineBreakReset (Z)Lio/matthewnelson/encoding/base32/Base32$Hex$Builder; public final fun padEncoded (Z)Lio/matthewnelson/encoding/base32/Base32$Hex$Builder; public final fun strictSpec ()Lio/matthewnelson/encoding/base32/Base32$Hex$Builder; } @@ -167,7 +169,7 @@ public final class io/matthewnelson/encoding/base32/Base32$Hex$Config : io/matth public final field encodeToLowercase Z public final field isConstantTime Z public final field padEncoded Z - public synthetic fun (ZBZZZLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (ZBZZZZLkotlin/jvm/internal/DefaultConstructorMarker;)V } public final class io/matthewnelson/encoding/base32/Base32CrockfordConfigBuilder { diff --git a/library/base32/api/base32.klib.api b/library/base32/api/base32.klib.api index 46b6f48a..1f126391 100644 --- a/library/base32/api/base32.klib.api +++ b/library/base32/api/base32.klib.api @@ -136,6 +136,7 @@ sealed class <#A: io.matthewnelson.encoding.core/EncoderDecoder.Config> io.matth final fun encodeLowercase(kotlin/Boolean): io.matthewnelson.encoding.base32/Base32.Default.Builder // io.matthewnelson.encoding.base32/Base32.Default.Builder.encodeLowercase|encodeLowercase(kotlin.Boolean){}[0] final fun isLenient(kotlin/Boolean): io.matthewnelson.encoding.base32/Base32.Default.Builder // io.matthewnelson.encoding.base32/Base32.Default.Builder.isLenient|isLenient(kotlin.Boolean){}[0] final fun lineBreak(kotlin/Byte): io.matthewnelson.encoding.base32/Base32.Default.Builder // io.matthewnelson.encoding.base32/Base32.Default.Builder.lineBreak|lineBreak(kotlin.Byte){}[0] + final fun lineBreakReset(kotlin/Boolean): io.matthewnelson.encoding.base32/Base32.Default.Builder // io.matthewnelson.encoding.base32/Base32.Default.Builder.lineBreakReset|lineBreakReset(kotlin.Boolean){}[0] final fun padEncoded(kotlin/Boolean): io.matthewnelson.encoding.base32/Base32.Default.Builder // io.matthewnelson.encoding.base32/Base32.Default.Builder.padEncoded|padEncoded(kotlin.Boolean){}[0] final fun strictSpec(): io.matthewnelson.encoding.base32/Base32.Default.Builder // io.matthewnelson.encoding.base32/Base32.Default.Builder.strictSpec|strictSpec(){}[0] } @@ -174,6 +175,7 @@ sealed class <#A: io.matthewnelson.encoding.core/EncoderDecoder.Config> io.matth final fun encodeLowercase(kotlin/Boolean): io.matthewnelson.encoding.base32/Base32.Hex.Builder // io.matthewnelson.encoding.base32/Base32.Hex.Builder.encodeLowercase|encodeLowercase(kotlin.Boolean){}[0] final fun isLenient(kotlin/Boolean): io.matthewnelson.encoding.base32/Base32.Hex.Builder // io.matthewnelson.encoding.base32/Base32.Hex.Builder.isLenient|isLenient(kotlin.Boolean){}[0] final fun lineBreak(kotlin/Byte): io.matthewnelson.encoding.base32/Base32.Hex.Builder // io.matthewnelson.encoding.base32/Base32.Hex.Builder.lineBreak|lineBreak(kotlin.Byte){}[0] + final fun lineBreakReset(kotlin/Boolean): io.matthewnelson.encoding.base32/Base32.Hex.Builder // io.matthewnelson.encoding.base32/Base32.Hex.Builder.lineBreakReset|lineBreakReset(kotlin.Boolean){}[0] final fun padEncoded(kotlin/Boolean): io.matthewnelson.encoding.base32/Base32.Hex.Builder // io.matthewnelson.encoding.base32/Base32.Hex.Builder.padEncoded|padEncoded(kotlin.Boolean){}[0] final fun strictSpec(): io.matthewnelson.encoding.base32/Base32.Hex.Builder // io.matthewnelson.encoding.base32/Base32.Hex.Builder.strictSpec|strictSpec(){}[0] } diff --git a/library/base32/src/commonMain/kotlin/io/matthewnelson/encoding/base32/Base32.kt b/library/base32/src/commonMain/kotlin/io/matthewnelson/encoding/base32/Base32.kt index ea0342bf..6511388a 100644 --- a/library/base32/src/commonMain/kotlin/io/matthewnelson/encoding/base32/Base32.kt +++ b/library/base32/src/commonMain/kotlin/io/matthewnelson/encoding/base32/Base32.kt @@ -56,6 +56,7 @@ public sealed class Base32(config: C): EncoderDecoder< * encodeLowercase(enable = false) * hyphen(interval = 5) * check(symbol = '~') + * backFillBuffers(enable = true) * } * * val text = "Hello World!" @@ -94,20 +95,20 @@ public sealed class Base32(config: C): EncoderDecoder< } @get:JvmSynthetic - @set:JvmSynthetic internal var _isLenient: Boolean = true + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _encodeLowercase: Boolean = false + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _hyphenInterval: Byte = 0 + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _checkSymbol: Char? = null + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _backFillBuffers: Boolean = true + private set // Here for compatibility purposes with Base32CrockfordConfigBuilder @get:JvmSynthetic @@ -231,6 +232,7 @@ public sealed class Base32(config: C): EncoderDecoder< ): EncoderDecoder.Config( isLenient, lineBreakInterval = 0, + lineBreakResetOnFlush = false, paddingChar = null, maxDecodeEmit = 5, backFillBuffers, @@ -413,7 +415,7 @@ public sealed class Base32(config: C): EncoderDecoder< config.checkSymbol?.let { symbol -> output(symbol.lowercaseChar()) } - (this as? HyphenOutFeed)?.reset() + (this as? HyphenOutFeed)?.reset(isFlush = !isClosed()) } } } @@ -427,7 +429,7 @@ public sealed class Base32(config: C): EncoderDecoder< config.checkSymbol?.let { symbol -> output(symbol.uppercaseChar()) } - (this as? HyphenOutFeed)?.reset() + (this as? HyphenOutFeed)?.reset(isFlush = !isClosed()) } } } @@ -456,8 +458,10 @@ public sealed class Base32(config: C): EncoderDecoder< * val default = Base32.Default.Builder { * isLenient(enable = true) * lineBreak(interval = 64) + * lineBreakReset(onFlush = true) * encodeLowercase(enable = false) * padEncoded(enable = true) + * backFillBuffers(enable = true) * } * * val text = "Hello World!" @@ -489,26 +493,30 @@ public sealed class Base32(config: C): EncoderDecoder< if (other == null) return this._isLenient = other.isLenient ?: true this._lineBreakInterval = other.lineBreakInterval + this._lineBreakResetOnFlush = other.lineBreakResetOnFlush this._encodeLowercase = other.encodeLowercase this._padEncoded = other.padEncoded this._backFillBuffers = other.backFillBuffers } @get:JvmSynthetic - @set:JvmSynthetic internal var _isLenient: Boolean = true + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _lineBreakInterval: Byte = 0 + private set + @get:JvmSynthetic + internal var _lineBreakResetOnFlush: Boolean = true + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _encodeLowercase: Boolean = false + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _padEncoded: Boolean = true + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _backFillBuffers: Boolean = true + private set /** * DEFAULT: `true` @@ -551,6 +559,13 @@ public sealed class Base32(config: C): EncoderDecoder< * */ public fun lineBreak(interval: Byte): Builder = apply { _lineBreakInterval = interval } + /** + * DEFAULT: `true` + * + * @see [EncoderDecoder.Config.lineBreakResetOnFlush] + * */ + public fun lineBreakReset(onFlush: Boolean): Builder = apply { _lineBreakResetOnFlush = onFlush } + /** * DEFAULT: `false` * @@ -614,6 +629,7 @@ public sealed class Base32(config: C): EncoderDecoder< public class Config private constructor( isLenient: Boolean, lineBreakInterval: Byte, + lineBreakResetOnFlush: Boolean, @JvmField public val encodeLowercase: Boolean, @JvmField @@ -622,6 +638,7 @@ public sealed class Base32(config: C): EncoderDecoder< ): EncoderDecoder.Config( isLenient, lineBreakInterval, + lineBreakResetOnFlush, paddingChar = '=', maxDecodeEmit = 5, backFillBuffers, @@ -654,6 +671,7 @@ public sealed class Base32(config: C): EncoderDecoder< internal val DEFAULT: Config = Config( isLenient = true, lineBreakInterval = 64, + lineBreakResetOnFlush = true, encodeLowercase = false, padEncoded = true, backFillBuffers = true, @@ -781,8 +799,10 @@ public sealed class Base32(config: C): EncoderDecoder< * val hex = Base32.Hex.Builder { * isLenient(enable = true) * lineBreak(interval = 64) + * lineBreakReset(onFlush = true) * encodeLowercase(enable = false) * padEncoded(enable = true) + * backFillBuffers(enable = true) * } * * val text = "Hello World!" @@ -814,26 +834,30 @@ public sealed class Base32(config: C): EncoderDecoder< if (other == null) return this._isLenient = other.isLenient ?: true this._lineBreakInterval = other.lineBreakInterval + this._lineBreakResetOnFlush = other.lineBreakResetOnFlush this._encodeLowercase = other.encodeLowercase this._padEncoded = other.padEncoded this._backFillBuffers = other.backFillBuffers } @get:JvmSynthetic - @set:JvmSynthetic internal var _isLenient: Boolean = true + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _lineBreakInterval: Byte = 0 + private set + @get:JvmSynthetic + internal var _lineBreakResetOnFlush: Boolean = true + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _encodeLowercase: Boolean = false + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _padEncoded: Boolean = true + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _backFillBuffers: Boolean = true + private set /** * DEFAULT: `true` @@ -876,6 +900,13 @@ public sealed class Base32(config: C): EncoderDecoder< * */ public fun lineBreak(interval: Byte): Builder = apply { _lineBreakInterval = interval } + /** + * DEFAULT: `true` + * + * @see [EncoderDecoder.Config.lineBreakResetOnFlush] + * */ + public fun lineBreakReset(onFlush: Boolean): Builder = apply { _lineBreakResetOnFlush = onFlush } + /** * DEFAULT: `false` * @@ -939,6 +970,7 @@ public sealed class Base32(config: C): EncoderDecoder< public class Config private constructor( isLenient: Boolean, lineBreakInterval: Byte, + lineBreakResetOnFlush: Boolean, @JvmField public val encodeLowercase: Boolean, @JvmField @@ -947,6 +979,7 @@ public sealed class Base32(config: C): EncoderDecoder< ): EncoderDecoder.Config( isLenient, lineBreakInterval, + lineBreakResetOnFlush, paddingChar = '=', maxDecodeEmit = 5, backFillBuffers, @@ -979,6 +1012,7 @@ public sealed class Base32(config: C): EncoderDecoder< internal val DEFAULT: Config = Config( isLenient = true, lineBreakInterval = 64, + lineBreakResetOnFlush = true, encodeLowercase = false, padEncoded = true, backFillBuffers = true, @@ -1541,9 +1575,11 @@ public sealed class Base32(config: C): EncoderDecoder< private var count: Byte = 0 - fun reset() { + fun reset(isFlush: Boolean) { count = 0 - if (out is LineBreakOutFeed) out.reset() + if (!isFlush) return + if (out !is LineBreakOutFeed) return + if (out.resetOnFlush) out.reset() } override fun output(encoded: Char) { diff --git a/library/base32/src/commonMain/kotlin/io/matthewnelson/encoding/base32/Builders.kt b/library/base32/src/commonMain/kotlin/io/matthewnelson/encoding/base32/Builders.kt index cf9cdae7..01d77198 100644 --- a/library/base32/src/commonMain/kotlin/io/matthewnelson/encoding/base32/Builders.kt +++ b/library/base32/src/commonMain/kotlin/io/matthewnelson/encoding/base32/Builders.kt @@ -347,6 +347,13 @@ public class Base32DefaultConfigBuilder { lineBreakInterval = compat._lineBreakInterval encodeToLowercase = compat._encodeLowercase padEncoded = compat._padEncoded + + // Prior to 2.6.0, behavior was broken whereby the + // LineBreakOutFeed was not being reset whenever + // Encoder.Feed.flush was called. This maintains + // that behavior for consumers who have not updated + // to the Builder API. + compat.lineBreakReset(onFlush = false) } /** @@ -430,6 +437,13 @@ public class Base32HexConfigBuilder { lineBreakInterval = compat._lineBreakInterval encodeToLowercase = compat._encodeLowercase padEncoded = compat._padEncoded + + // Prior to 2.6.0, behavior was broken whereby the + // LineBreakOutFeed was not being reset whenever + // Encoder.Feed.flush was called. This maintains + // that behavior for consumers who have not updated + // to the Builder API. + compat.lineBreakReset(onFlush = false) } /** diff --git a/library/base32/src/commonMain/kotlin/io/matthewnelson/encoding/base32/internal/-Config.kt b/library/base32/src/commonMain/kotlin/io/matthewnelson/encoding/base32/internal/-Config.kt index 31bd4e0c..b88cfe98 100644 --- a/library/base32/src/commonMain/kotlin/io/matthewnelson/encoding/base32/internal/-Config.kt +++ b/library/base32/src/commonMain/kotlin/io/matthewnelson/encoding/base32/internal/-Config.kt @@ -45,13 +45,14 @@ internal inline fun ((Boolean, Boolean, Byte, Char?, Boolean, Boolean) -> Base32 return crockford(config, null) } -internal inline fun ((Boolean, Byte, Boolean, Boolean, Boolean) -> Base32.Default.Config).build( +internal inline fun ((Boolean, Byte, Boolean, Boolean, Boolean, Boolean) -> Base32.Default.Config).build( b: Base32.Default.Builder, noinline default: (Base32.Default.Config, Any?) -> Base32.Default, ): Base32.Default { if ( b._isLenient == Base32.Default.DELEGATE.config.isLenient && b._lineBreakInterval == Base32.Default.DELEGATE.config.lineBreakInterval + && b._lineBreakResetOnFlush == Base32.Default.DELEGATE.config.lineBreakResetOnFlush && b._encodeLowercase == Base32.Default.DELEGATE.config.encodeLowercase && b._padEncoded == Base32.Default.DELEGATE.config.padEncoded && b._backFillBuffers == Base32.Default.DELEGATE.config.backFillBuffers @@ -61,6 +62,7 @@ internal inline fun ((Boolean, Byte, Boolean, Boolean, Boolean) -> Base32.Defaul val config = this( b._isLenient, b._lineBreakInterval, + b._lineBreakResetOnFlush, b._encodeLowercase, b._padEncoded, b._backFillBuffers, @@ -68,13 +70,14 @@ internal inline fun ((Boolean, Byte, Boolean, Boolean, Boolean) -> Base32.Defaul return default(config, null) } -internal inline fun ((Boolean, Byte, Boolean, Boolean, Boolean) -> Base32.Hex.Config).build( +internal inline fun ((Boolean, Byte, Boolean, Boolean, Boolean, Boolean) -> Base32.Hex.Config).build( b: Base32.Hex.Builder, noinline hex: (Base32.Hex.Config, Any?) -> Base32.Hex, ): Base32.Hex { if ( b._isLenient == Base32.Hex.DELEGATE.config.isLenient && b._lineBreakInterval == Base32.Hex.DELEGATE.config.lineBreakInterval + && b._lineBreakResetOnFlush == Base32.Hex.DELEGATE.config.lineBreakResetOnFlush && b._encodeLowercase == Base32.Hex.DELEGATE.config.encodeLowercase && b._padEncoded == Base32.Hex.DELEGATE.config.padEncoded && b._backFillBuffers == Base32.Hex.DELEGATE.config.backFillBuffers @@ -84,6 +87,7 @@ internal inline fun ((Boolean, Byte, Boolean, Boolean, Boolean) -> Base32.Hex.Co val config = this( b._isLenient, b._lineBreakInterval, + b._lineBreakResetOnFlush, b._encodeLowercase, b._padEncoded, b._backFillBuffers, diff --git a/library/base64/README.md b/library/base64/README.md index 2f19a1b5..5dc63a94 100644 --- a/library/base64/README.md +++ b/library/base64/README.md @@ -6,8 +6,10 @@ Base64 encoding/decoding in accordance with [RFC 4648 section 4][url-rfc-4], [RF val base64 = Base64.Builder { isLenient(enable = true) lineBreak(interval = 64) + lineBreakReset(onFlush = true) encodeUrlSafe(enable = false) padEncoded(enable = true) + backFillBuffers(enable = true) } val text = "Hello World!" diff --git a/library/base64/api/base64.api b/library/base64/api/base64.api index d4cf5b8a..6c67743d 100644 --- a/library/base64/api/base64.api +++ b/library/base64/api/base64.api @@ -57,6 +57,7 @@ public final class io/matthewnelson/encoding/base64/Base64$Builder { public final fun encodeUrlSafe (Z)Lio/matthewnelson/encoding/base64/Base64$Builder; public final fun isLenient (Z)Lio/matthewnelson/encoding/base64/Base64$Builder; public final fun lineBreak (B)Lio/matthewnelson/encoding/base64/Base64$Builder; + public final fun lineBreakReset (Z)Lio/matthewnelson/encoding/base64/Base64$Builder; public final fun padEncoded (Z)Lio/matthewnelson/encoding/base64/Base64$Builder; public final fun strictSpec ()Lio/matthewnelson/encoding/base64/Base64$Builder; } @@ -71,7 +72,7 @@ public final class io/matthewnelson/encoding/base64/Base64$Config : io/matthewne public final field encodeUrlSafe Z public final field isConstantTime Z public final field padEncoded Z - public synthetic fun (ZBZZZLkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (ZBZZZZLkotlin/jvm/internal/DefaultConstructorMarker;)V } public final class io/matthewnelson/encoding/base64/Base64$Default : io/matthewnelson/encoding/core/EncoderDecoder { diff --git a/library/base64/api/base64.klib.api b/library/base64/api/base64.klib.api index ebd004b4..874da94d 100644 --- a/library/base64/api/base64.klib.api +++ b/library/base64/api/base64.klib.api @@ -18,6 +18,7 @@ final class io.matthewnelson.encoding.base64/Base64 : io.matthewnelson.encoding. final fun encodeUrlSafe(kotlin/Boolean): io.matthewnelson.encoding.base64/Base64.Builder // io.matthewnelson.encoding.base64/Base64.Builder.encodeUrlSafe|encodeUrlSafe(kotlin.Boolean){}[0] final fun isLenient(kotlin/Boolean): io.matthewnelson.encoding.base64/Base64.Builder // io.matthewnelson.encoding.base64/Base64.Builder.isLenient|isLenient(kotlin.Boolean){}[0] final fun lineBreak(kotlin/Byte): io.matthewnelson.encoding.base64/Base64.Builder // io.matthewnelson.encoding.base64/Base64.Builder.lineBreak|lineBreak(kotlin.Byte){}[0] + final fun lineBreakReset(kotlin/Boolean): io.matthewnelson.encoding.base64/Base64.Builder // io.matthewnelson.encoding.base64/Base64.Builder.lineBreakReset|lineBreakReset(kotlin.Boolean){}[0] final fun padEncoded(kotlin/Boolean): io.matthewnelson.encoding.base64/Base64.Builder // io.matthewnelson.encoding.base64/Base64.Builder.padEncoded|padEncoded(kotlin.Boolean){}[0] final fun strictSpec(): io.matthewnelson.encoding.base64/Base64.Builder // io.matthewnelson.encoding.base64/Base64.Builder.strictSpec|strictSpec(){}[0] } diff --git a/library/base64/src/commonMain/kotlin/io/matthewnelson/encoding/base64/Base64.kt b/library/base64/src/commonMain/kotlin/io/matthewnelson/encoding/base64/Base64.kt index 6047148e..369c1af7 100644 --- a/library/base64/src/commonMain/kotlin/io/matthewnelson/encoding/base64/Base64.kt +++ b/library/base64/src/commonMain/kotlin/io/matthewnelson/encoding/base64/Base64.kt @@ -44,8 +44,10 @@ import kotlin.jvm.JvmSynthetic * val base64 = Base64.Builder { * isLenient(enable = true) * lineBreak(interval = 64) + * lineBreakReset(onFlush = true) * encodeUrlSafe(enable = false) * padEncoded(enable = true) + * backFillBuffers(enable = true) * } * * val text = "Hello World!" @@ -108,26 +110,30 @@ public class Base64: EncoderDecoder { if (other == null) return this._isLenient = other.isLenient ?: true this._lineBreakInterval = other.lineBreakInterval + this._lineBreakResetOnFlush = other.lineBreakResetOnFlush this._encodeUrlSafe = other.encodeUrlSafe this._padEncoded = other.padEncoded this._backFillBuffers = other.backFillBuffers } @get:JvmSynthetic - @set:JvmSynthetic internal var _isLenient: Boolean = true + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _lineBreakInterval: Byte = 0 + private set + @get:JvmSynthetic + internal var _lineBreakResetOnFlush: Boolean = true + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _encodeUrlSafe: Boolean = false + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _padEncoded: Boolean = true + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _backFillBuffers: Boolean = true + private set /** * DEFAULT: `true` @@ -170,6 +176,13 @@ public class Base64: EncoderDecoder { * */ public fun lineBreak(interval: Byte): Builder = apply { _lineBreakInterval = interval } + /** + * DEFAULT: `true` + * + * @see [EncoderDecoder.Config.lineBreakResetOnFlush] + * */ + public fun lineBreakReset(onFlush: Boolean): Builder = apply { _lineBreakResetOnFlush = onFlush } + /** * DEFAULT: `false` * @@ -229,6 +242,7 @@ public class Base64: EncoderDecoder { public class Config private constructor( isLenient: Boolean, lineBreakInterval: Byte, + lineBreakResetOnFlush: Boolean, @JvmField public val encodeUrlSafe: Boolean, @JvmField @@ -237,6 +251,7 @@ public class Base64: EncoderDecoder { ): EncoderDecoder.Config( isLenient, lineBreakInterval, + lineBreakResetOnFlush, paddingChar = '=', maxDecodeEmit = 3, backFillBuffers, @@ -287,6 +302,7 @@ public class Base64: EncoderDecoder { internal val DEFAULT: Config = Config( isLenient = true, lineBreakInterval = 64, + lineBreakResetOnFlush = true, encodeUrlSafe = false, padEncoded = true, backFillBuffers = true, @@ -296,6 +312,7 @@ public class Base64: EncoderDecoder { internal val URL_SAFE: Config = Config( isLenient = DEFAULT.isLenient ?: true, lineBreakInterval = DEFAULT.lineBreakInterval, + lineBreakResetOnFlush = DEFAULT.lineBreakResetOnFlush, encodeUrlSafe = true, padEncoded = DEFAULT.padEncoded, backFillBuffers = DEFAULT.backFillBuffers diff --git a/library/base64/src/commonMain/kotlin/io/matthewnelson/encoding/base64/Builders.kt b/library/base64/src/commonMain/kotlin/io/matthewnelson/encoding/base64/Builders.kt index 2b41806a..4c4661ee 100644 --- a/library/base64/src/commonMain/kotlin/io/matthewnelson/encoding/base64/Builders.kt +++ b/library/base64/src/commonMain/kotlin/io/matthewnelson/encoding/base64/Builders.kt @@ -86,6 +86,13 @@ public class Base64ConfigBuilder { lineBreakInterval = compat._lineBreakInterval encodeToUrlSafe = compat._encodeUrlSafe padEncoded = compat._padEncoded + + // Prior to 2.6.0, behavior was broken whereby the + // LineBreakOutFeed was not being reset whenever + // Encoder.Feed.flush was called. This maintains + // that behavior for consumers who have not updated + // to the Builder API. + compat.lineBreakReset(onFlush = false) } /** diff --git a/library/base64/src/commonMain/kotlin/io/matthewnelson/encoding/base64/internal/-Config.kt b/library/base64/src/commonMain/kotlin/io/matthewnelson/encoding/base64/internal/-Config.kt index 03834206..0b47a173 100644 --- a/library/base64/src/commonMain/kotlin/io/matthewnelson/encoding/base64/internal/-Config.kt +++ b/library/base64/src/commonMain/kotlin/io/matthewnelson/encoding/base64/internal/-Config.kt @@ -19,13 +19,14 @@ package io.matthewnelson.encoding.base64.internal import io.matthewnelson.encoding.base64.Base64 -internal inline fun ((Boolean, Byte, Boolean, Boolean, Boolean) -> Base64.Config).build( +internal inline fun ((Boolean, Byte, Boolean, Boolean, Boolean, Boolean) -> Base64.Config).build( b: Base64.Builder, noinline base64: (Base64.Config, Any?) -> Base64, ): Base64 { if ( b._isLenient == Base64.Default.DELEGATE.config.isLenient && b._lineBreakInterval == Base64.Default.DELEGATE.config.lineBreakInterval + && b._lineBreakResetOnFlush == Base64.Default.DELEGATE.config.lineBreakResetOnFlush && b._padEncoded == Base64.Default.DELEGATE.config.padEncoded && b._backFillBuffers == Base64.Default.DELEGATE.config.backFillBuffers ) { @@ -34,6 +35,7 @@ internal inline fun ((Boolean, Byte, Boolean, Boolean, Boolean) -> Base64.Config val config = this( b._isLenient, b._lineBreakInterval, + b._lineBreakResetOnFlush, b._encodeUrlSafe, b._padEncoded, b._backFillBuffers, diff --git a/library/core/README.md b/library/core/README.md index 44ee06bd..f21afb52 100644 --- a/library/core/README.md +++ b/library/core/README.md @@ -15,11 +15,13 @@ fun main() { // Define the callback for where to dump encoded characters as they // come out of the Encoder.Feed + // + // Alternatively, use Encoder.OutFeed(sb::append) val out = Encoder.OutFeed { char -> sb.append(char) } // Wrap it in helper LineBreakOutFeed which will output `\n` every `interval` // characters of output. - val outN = LineBreakOutFeed(interval = 64, out) + val outN = LineBreakOutFeed(interval = 64, resetOnFlush = false, out) Base64.Default.newEncoderFeed(outN).use { feed -> // Encode UTF-8 bytes to base64 diff --git a/library/core/api/core.api b/library/core/api/core.api index 59d6f517..c47dc7f5 100644 --- a/library/core/api/core.api +++ b/library/core/api/core.api @@ -122,10 +122,11 @@ public abstract class io/matthewnelson/encoding/core/EncoderDecoder$Config { public final field backFillBuffers Z public final field isLenient Ljava/lang/Boolean; public final field lineBreakInterval B + public final field lineBreakResetOnFlush Z public final field maxDecodeEmit I public final field paddingChar Ljava/lang/Character; public fun (Ljava/lang/Boolean;BLjava/lang/Character;)V - protected fun (Ljava/lang/Boolean;BLjava/lang/Character;IZ)V + protected fun (Ljava/lang/Boolean;BZLjava/lang/Character;IZ)V public final fun decodeOutMaxSize (J)J public final fun decodeOutMaxSizeOrFail (Lio/matthewnelson/encoding/core/util/DecoderInput;)I protected abstract fun decodeOutMaxSizeOrFailProtected (ILio/matthewnelson/encoding/core/util/DecoderInput;)I @@ -236,7 +237,9 @@ public abstract interface class io/matthewnelson/encoding/core/util/FeedBuffer$F public final class io/matthewnelson/encoding/core/util/LineBreakOutFeed : io/matthewnelson/encoding/core/Encoder$OutFeed { public final field interval B + public final field resetOnFlush Z public fun (BLio/matthewnelson/encoding/core/Encoder$OutFeed;)V + public fun (BZLio/matthewnelson/encoding/core/Encoder$OutFeed;)V public final fun count ()B public fun output (C)V public final fun reset ()V diff --git a/library/core/api/core.klib.api b/library/core/api/core.klib.api index a211ddc4..6924db4b 100644 --- a/library/core/api/core.klib.api +++ b/library/core/api/core.klib.api @@ -36,8 +36,8 @@ abstract class <#A: io.matthewnelson.encoding.core/EncoderDecoder.Config> io.mat final fun toString(): kotlin/String // io.matthewnelson.encoding.core/EncoderDecoder.toString|toString(){}[0] abstract class Config { // io.matthewnelson.encoding.core/EncoderDecoder.Config|null[0] + constructor (kotlin/Boolean?, kotlin/Byte, kotlin/Boolean, kotlin/Char?, kotlin/Int, kotlin/Boolean) // io.matthewnelson.encoding.core/EncoderDecoder.Config.|(kotlin.Boolean?;kotlin.Byte;kotlin.Boolean;kotlin.Char?;kotlin.Int;kotlin.Boolean){}[0] constructor (kotlin/Boolean?, kotlin/Byte, kotlin/Char?) // io.matthewnelson.encoding.core/EncoderDecoder.Config.|(kotlin.Boolean?;kotlin.Byte;kotlin.Char?){}[0] - constructor (kotlin/Boolean?, kotlin/Byte, kotlin/Char?, kotlin/Int, kotlin/Boolean) // io.matthewnelson.encoding.core/EncoderDecoder.Config.|(kotlin.Boolean?;kotlin.Byte;kotlin.Char?;kotlin.Int;kotlin.Boolean){}[0] final val backFillBuffers // io.matthewnelson.encoding.core/EncoderDecoder.Config.backFillBuffers|{}backFillBuffers[0] final fun (): kotlin/Boolean // io.matthewnelson.encoding.core/EncoderDecoder.Config.backFillBuffers.|(){}[0] @@ -45,6 +45,8 @@ abstract class <#A: io.matthewnelson.encoding.core/EncoderDecoder.Config> io.mat final fun (): kotlin/Boolean? // io.matthewnelson.encoding.core/EncoderDecoder.Config.isLenient.|(){}[0] final val lineBreakInterval // io.matthewnelson.encoding.core/EncoderDecoder.Config.lineBreakInterval|{}lineBreakInterval[0] final fun (): kotlin/Byte // io.matthewnelson.encoding.core/EncoderDecoder.Config.lineBreakInterval.|(){}[0] + final val lineBreakResetOnFlush // io.matthewnelson.encoding.core/EncoderDecoder.Config.lineBreakResetOnFlush|{}lineBreakResetOnFlush[0] + final fun (): kotlin/Boolean // io.matthewnelson.encoding.core/EncoderDecoder.Config.lineBreakResetOnFlush.|(){}[0] final val maxDecodeEmit // io.matthewnelson.encoding.core/EncoderDecoder.Config.maxDecodeEmit|{}maxDecodeEmit[0] final fun (): kotlin/Int // io.matthewnelson.encoding.core/EncoderDecoder.Config.maxDecodeEmit.|(){}[0] final val paddingChar // io.matthewnelson.encoding.core/EncoderDecoder.Config.paddingChar|{}paddingChar[0] @@ -146,9 +148,12 @@ final class io.matthewnelson.encoding.core.util/DecoderInput { // io.matthewnels final class io.matthewnelson.encoding.core.util/LineBreakOutFeed : io.matthewnelson.encoding.core/Encoder.OutFeed { // io.matthewnelson.encoding.core.util/LineBreakOutFeed|null[0] constructor (kotlin/Byte, io.matthewnelson.encoding.core/Encoder.OutFeed) // io.matthewnelson.encoding.core.util/LineBreakOutFeed.|(kotlin.Byte;io.matthewnelson.encoding.core.Encoder.OutFeed){}[0] + constructor (kotlin/Byte, kotlin/Boolean, io.matthewnelson.encoding.core/Encoder.OutFeed) // io.matthewnelson.encoding.core.util/LineBreakOutFeed.|(kotlin.Byte;kotlin.Boolean;io.matthewnelson.encoding.core.Encoder.OutFeed){}[0] final val interval // io.matthewnelson.encoding.core.util/LineBreakOutFeed.interval|{}interval[0] final fun (): kotlin/Byte // io.matthewnelson.encoding.core.util/LineBreakOutFeed.interval.|(){}[0] + final val resetOnFlush // io.matthewnelson.encoding.core.util/LineBreakOutFeed.resetOnFlush|{}resetOnFlush[0] + final fun (): kotlin/Boolean // io.matthewnelson.encoding.core.util/LineBreakOutFeed.resetOnFlush.|(){}[0] final var count // io.matthewnelson.encoding.core.util/LineBreakOutFeed.count|{}count[0] final fun (): kotlin/Byte // io.matthewnelson.encoding.core.util/LineBreakOutFeed.count.|(){}[0] diff --git a/library/core/src/commonMain/kotlin/io/matthewnelson/encoding/core/Encoder.kt b/library/core/src/commonMain/kotlin/io/matthewnelson/encoding/core/Encoder.kt index 9c4c8e72..59eaa583 100644 --- a/library/core/src/commonMain/kotlin/io/matthewnelson/encoding/core/Encoder.kt +++ b/library/core/src/commonMain/kotlin/io/matthewnelson/encoding/core/Encoder.kt @@ -39,7 +39,7 @@ public sealed class Encoder(config: C): Decoder(con /** * Creates a new [Encoder.Feed], outputting encoded data to the supplied [Encoder.OutFeed]. * - * **NOTE:** The supplied [Encoder.OutFeed] will be wrapped in [LineBreakOutFeed] (if not + * **NOTE:** The supplied [Encoder.OutFeed] will be wrapped in a [LineBreakOutFeed] (if not * already one) when [EncoderDecoder.Config.lineBreakInterval] is greater than `0`. * * e.g. @@ -58,10 +58,12 @@ public sealed class Encoder(config: C): Decoder(con * * @see [Encoder.Feed] * @see [LineBreakOutFeed] + * @see [EncoderDecoder.Config.lineBreakInterval] + * @see [EncoderDecoder.Config.lineBreakResetOnFlush] * */ public fun newEncoderFeed(out: Encoder.OutFeed): Encoder.Feed { val _out = if (config.lineBreakInterval > 0 && out !is LineBreakOutFeed) { - LineBreakOutFeed(config.lineBreakInterval, out) + LineBreakOutFeed(config.lineBreakInterval, config.lineBreakResetOnFlush, out) } else { out } @@ -97,6 +99,10 @@ public sealed class Encoder(config: C): Decoder(con * is set to the [Encoder.OutFeed.NoOp] instance which ensures any local * object references that the initial [OutFeed] has are not leaked and can * be promptly GCd. + * + * **NOTE:** [LineBreakOutFeed.resetOnFlush] functionality relies on [Encoder] + * implementations instantiating their [Feed] with the [Encoder.OutFeed] + * provided to [newEncoderFeedProtected]. * */ @get:JvmName("_out") protected var _out: Encoder.OutFeed @@ -137,7 +143,8 @@ public sealed class Encoder(config: C): Decoder(con try { doFinalProtected() - (_out as? LineBreakOutFeed)?.reset() + val lbf = (_out as? LineBreakOutFeed) ?: return + if (lbf.resetOnFlush) lbf.reset() } catch (t: Throwable) { close() throw t @@ -146,7 +153,6 @@ public sealed class Encoder(config: C): Decoder(con public final override fun close() { _isClosed = true - (_out as? LineBreakOutFeed)?.reset() _out = OutFeed.NoOp } diff --git a/library/core/src/commonMain/kotlin/io/matthewnelson/encoding/core/EncoderDecoder.kt b/library/core/src/commonMain/kotlin/io/matthewnelson/encoding/core/EncoderDecoder.kt index aed4195c..3cb304d8 100644 --- a/library/core/src/commonMain/kotlin/io/matthewnelson/encoding/core/EncoderDecoder.kt +++ b/library/core/src/commonMain/kotlin/io/matthewnelson/encoding/core/EncoderDecoder.kt @@ -70,6 +70,18 @@ public abstract class EncoderDecoder(config: C): Encod @JvmField public val lineBreakInterval: Byte, + /** + * If and only if [Encoder.newEncoderFeed] wraps the [Encoder.OutFeed] passed to it with a + * [LineBreakOutFeed], will this setting be used for [LineBreakOutFeed.resetOnFlush]. If + * `true` and [Encoder.newEncoderFeed] wrapped its provided [Encoder.OutFeed], then + * [LineBreakOutFeed.reset] will be called after every invocation of [Encoder.Feed.flush]. + * + * @see [LineBreakOutFeed] + * @see [Encoder.newEncoderFeed] + * */ + @JvmField + public val lineBreakResetOnFlush: Boolean, + /** * The character that is used when padding encoded output. This is used by [Decoder.Feed] * to mark input as "completing" such that further non-padding input can be exceptionally @@ -120,7 +132,7 @@ public abstract class EncoderDecoder(config: C): Encod public val backFillBuffers: Boolean, // NOTE: Adding any parameters requires updating equals/hashCode/toString - @Suppress("UNUSED", "UNUSED_PARAMETER") unused: Any?, + @Suppress("UNUSED_PARAMETER") unused: Any?, ) { /** @@ -131,12 +143,14 @@ public abstract class EncoderDecoder(config: C): Encod protected constructor( isLenient: Boolean?, lineBreakInterval: Byte, + lineBreakResetOnFlush: Boolean, paddingChar: Char?, maxDecodeEmit: Int, backFillBuffers: Boolean, ): this( isLenient = isLenient, lineBreakInterval = lineBreakIntervalOrZero(isLenient, lineBreakInterval), + lineBreakResetOnFlush = lineBreakResetOnFlush, paddingChar = paddingChar, maxDecodeEmit = maxDecodeEmit, backFillBuffers = backFillBuffers, @@ -412,6 +426,7 @@ public abstract class EncoderDecoder(config: C): Encod if (other !is Config) return false if (other.isLenient != this.isLenient) return false if (other.lineBreakInterval != this.lineBreakInterval) return false + if (other.lineBreakResetOnFlush != this.lineBreakResetOnFlush) return false if (other.paddingChar != this.paddingChar) return false if (other.maxDecodeEmit != this.maxDecodeEmit) return false if (other.backFillBuffers != this.backFillBuffers) return false @@ -424,6 +439,7 @@ public abstract class EncoderDecoder(config: C): Encod var result = 17 result = result * 31 + isLenient.hashCode() result = result * 31 + lineBreakInterval.hashCode() + result = result * 31 + lineBreakResetOnFlush.hashCode() result = result * 31 + paddingChar.hashCode() result = result * 31 + maxDecodeEmit.hashCode() result = result * 31 + backFillBuffers.hashCode() @@ -439,6 +455,8 @@ public abstract class EncoderDecoder(config: C): Encod appendLine(isLenient) append(" lineBreakInterval: ") appendLine(lineBreakInterval) + append(" lineBreakResetOnFlush: ") + appendLine(lineBreakResetOnFlush) append(" paddingChar: ") appendLine(paddingChar) append(" maxDecodeEmit: ") @@ -501,9 +519,9 @@ public abstract class EncoderDecoder(config: C): Encod * @suppress * */ @Deprecated( - message = "Parameters maxDecodeEmit and backFillBuffers were added. Use the new constructor.", + message = "Parameters, lineBreakResetOnFlush, maxDecodeEmit, and backFillBuffers were added. Use the new constructor.", replaceWith = ReplaceWith( - expression = "EncoderDecoder.Config(isLenient, lineBreakInterval, paddingChar, maxDecodeEmit = 0 /* TODO */, backFillBuffers = true)"), + expression = "EncoderDecoder.Config(isLenient, lineBreakInterval, lineBreakResetOnFlush = false, paddingChar, maxDecodeEmit = 0 /* TODO */, backFillBuffers = true)"), level = DeprecationLevel.WARNING, ) public constructor( @@ -513,6 +531,7 @@ public abstract class EncoderDecoder(config: C): Encod ): this( isLenient = isLenient, lineBreakInterval = lineBreakIntervalOrZero(isLenient, lineBreakInterval), + lineBreakResetOnFlush = false, paddingChar = paddingChar, maxDecodeEmit = -1, // NOTE: NEVER change. backFillBuffers = true, diff --git a/library/core/src/commonMain/kotlin/io/matthewnelson/encoding/core/util/LineBreakOutFeed.kt b/library/core/src/commonMain/kotlin/io/matthewnelson/encoding/core/util/LineBreakOutFeed.kt index 093841ed..25254ac7 100644 --- a/library/core/src/commonMain/kotlin/io/matthewnelson/encoding/core/util/LineBreakOutFeed.kt +++ b/library/core/src/commonMain/kotlin/io/matthewnelson/encoding/core/util/LineBreakOutFeed.kt @@ -16,6 +16,7 @@ package io.matthewnelson.encoding.core.util import io.matthewnelson.encoding.core.Encoder +import io.matthewnelson.encoding.core.EncoderDecoder import kotlin.jvm.JvmField import kotlin.jvm.JvmName @@ -23,29 +24,35 @@ import kotlin.jvm.JvmName * A Wrapper around another [Encoder.OutFeed] to hijack the output and insert * new line characters at every expressed [interval]. * - * @param [interval] The interval at which new lines are inserted - * @param [out] The other [Encoder.OutFeed] + * @param [interval] The interval at which new lines are output. + * @param [resetOnFlush] When `true` [Encoder.Feed.flush] will call [reset]. + * This setting has **no** effect if the [Encoder.Feed] implementation does + * not pass in their supplied [Encoder.OutFeed] as a constructor parameter + * to set [Encoder.Feed._out]. + * @param [out] The other [Encoder.OutFeed]. * - * @throws [IllegalArgumentException] if [interval] is less than 0 + * @see [Encoder.newEncoderFeed] + * @see [EncoderDecoder.Config.lineBreakInterval] + * @see [EncoderDecoder.Config.lineBreakResetOnFlush] + * + * @throws [IllegalArgumentException] If [interval] is less than `0`. * */ -public class LineBreakOutFeed -@Throws(IllegalArgumentException::class) -public constructor( +public class LineBreakOutFeed( @JvmField public val interval: Byte, + @JvmField + public val resetOnFlush: Boolean, private val out: Encoder.OutFeed, ): Encoder.OutFeed { - init { - require(interval > 0) { "interval must be greater than 0" } - } + init { require(interval > 0) { "interval must be greater than 0" } } @get:JvmName("count") public var count: Byte = 0 private set /** - * Resets the [count] to 0 + * Resets the [count] to `0`. * */ public fun reset() { count = 0 } @@ -58,4 +65,16 @@ public constructor( out.output(encoded) count++ } + + /** + * DEPRECATED since `2.6.0` + * @suppress + * */ + @Deprecated( + message = "Parameter resetOnFlush was added. Use the new constructor.", + replaceWith = ReplaceWith("LineBreakOutFeed(interval, resetOnFlush = false, out)"), + level = DeprecationLevel.WARNING, + ) + @Throws(IllegalArgumentException::class) + public constructor(interval: Byte, out: Encoder.OutFeed): this(interval, resetOnFlush = false, out) } diff --git a/library/core/src/commonTest/kotlin/io/matthewnelson/encoding/core/EncoderDecoderFeedUnitTest.kt b/library/core/src/commonTest/kotlin/io/matthewnelson/encoding/core/EncoderDecoderFeedUnitTest.kt index fc8e2727..c4e96074 100644 --- a/library/core/src/commonTest/kotlin/io/matthewnelson/encoding/core/EncoderDecoderFeedUnitTest.kt +++ b/library/core/src/commonTest/kotlin/io/matthewnelson/encoding/core/EncoderDecoderFeedUnitTest.kt @@ -264,7 +264,7 @@ class EncoderDecoderFeedUnitTest { ) val sb = StringBuilder() - encoder.newEncoderFeed(LineBreakOutFeed(10) { c -> + encoder.newEncoderFeed(LineBreakOutFeed(10, resetOnFlush = false) { c -> sb.append(c) }).use { feed -> assertEquals(0, sb.length) @@ -305,15 +305,24 @@ class EncoderDecoderFeedUnitTest { } @Test - fun givenEncoderFeed_whenFlushed_thenLineBreakOutFeedIsReset() { - val lbf = LineBreakOutFeed(Byte.MAX_VALUE) {} - val feed = TestEncoderDecoder(TestConfig()).newEncoderFeed(lbf) - assertEquals(0, lbf.count) - feed.consume(2) - feed.consume(2) - assertEquals(2, lbf.count) - feed.flush() - assertEquals(0, lbf.count) + fun givenEncoderFeed_whenFlushed_thenResetsLineBreakOutFeedAsExpected() { + val lbfTrue = LineBreakOutFeed(Byte.MAX_VALUE, resetOnFlush = true) {} + val feedTrue = TestEncoderDecoder(TestConfig()).newEncoderFeed(lbfTrue) + assertEquals(0, lbfTrue.count) + feedTrue.consume(2) + feedTrue.consume(2) + assertEquals(2, lbfTrue.count) + feedTrue.flush() + assertEquals(0, lbfTrue.count) + + val lbfFalse = LineBreakOutFeed(Byte.MAX_VALUE, resetOnFlush = false) {} + val feedFalse = TestEncoderDecoder(TestConfig()).newEncoderFeed(lbfFalse) + assertEquals(0, lbfFalse.count) + feedFalse.consume(2) + feedFalse.consume(2) + assertEquals(2, lbfFalse.count) + feedFalse.flush() + assertEquals(3, lbfFalse.count) } @Test diff --git a/library/core/src/commonTest/kotlin/io/matthewnelson/encoding/core/helpers/TestConfig.kt b/library/core/src/commonTest/kotlin/io/matthewnelson/encoding/core/helpers/TestConfig.kt index fb9e2034..d6111c7b 100644 --- a/library/core/src/commonTest/kotlin/io/matthewnelson/encoding/core/helpers/TestConfig.kt +++ b/library/core/src/commonTest/kotlin/io/matthewnelson/encoding/core/helpers/TestConfig.kt @@ -13,20 +13,30 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ +@file:Suppress("RedundantVisibilityModifier") + package io.matthewnelson.encoding.core.helpers import io.matthewnelson.encoding.core.EncoderDecoder import io.matthewnelson.encoding.core.util.DecoderInput -class TestConfig( +class TestConfig public constructor( isLenient: Boolean? = false, lineBreakInterval: Byte = 0, + lineBreakResetOnFlush: Boolean = true, paddingChar: Char? = '=', maxDecodeEmit: Int = 1, private val encodeReturn: (unEncodedSize: Long) -> Long = { -1L }, private val decodeInputReturn: (encodedSize: Int) -> Int = { -1 }, private val decodeReturn: (encodedSize: Long) -> Long = { -1L }, -): EncoderDecoder.Config(isLenient, lineBreakInterval, paddingChar, maxDecodeEmit, true) { +): EncoderDecoder.Config( + isLenient, + lineBreakInterval, + lineBreakResetOnFlush, + paddingChar, + maxDecodeEmit, + backFillBuffers = true, +) { override fun decodeOutMaxSizeProtected(encodedSize: Long): Long { return decodeReturn.invoke(encodedSize) } diff --git a/library/utf8/README.md b/library/utf8/README.md index f6201843..784d1fc6 100644 --- a/library/utf8/README.md +++ b/library/utf8/README.md @@ -5,6 +5,7 @@ UTF-8 encoding/decoding in accordance with [RFC 3629][url-rfc] ```kotlin val utf8 = UTF8.Builder { replacement(strategy = UTF8.ReplacementStrategy.KOTLIN) + backFillBuffers(enable = true) } val text = "Hello World!" diff --git a/library/utf8/src/commonMain/kotlin/io/matthewnelson/encoding/utf8/UTF8.kt b/library/utf8/src/commonMain/kotlin/io/matthewnelson/encoding/utf8/UTF8.kt index 0d231e1a..f9b5b826 100644 --- a/library/utf8/src/commonMain/kotlin/io/matthewnelson/encoding/utf8/UTF8.kt +++ b/library/utf8/src/commonMain/kotlin/io/matthewnelson/encoding/utf8/UTF8.kt @@ -45,6 +45,7 @@ import kotlin.jvm.JvmSynthetic * * val utf8 = UTF8.Builder { * replacement(strategy = UTF8.ReplacementStrategy.KOTLIN) + * backFillBuffers(enable = true) * } * * val text = "Hello World!" @@ -120,11 +121,11 @@ public open class UTF8: EncoderDecoder { } @get:JvmSynthetic - @set:JvmSynthetic internal var _replacementStrategy = ReplacementStrategy.KOTLIN + private set @get:JvmSynthetic - @set:JvmSynthetic internal var _backFillBuffers = true + private set /** * DEFAULT: [ReplacementStrategy.KOTLIN] @@ -235,7 +236,8 @@ public open class UTF8: EncoderDecoder { backFillBuffers: Boolean, ): EncoderDecoder.Config( isLenient = null, - lineBreakInterval = -1, + lineBreakInterval = 0, + lineBreakResetOnFlush = false, paddingChar = null, maxDecodeEmit = (replacementStrategy.size * 2).coerceAtLeast(4), backFillBuffers,