Add encode and decode padding options
diff --git a/libraries/stdlib/api/js/kotlin.io.encoding.kt b/libraries/stdlib/api/js/kotlin.io.encoding.kt
index 8241b5b..51af4ed 100644
--- a/libraries/stdlib/api/js/kotlin.io.encoding.kt
+++ b/libraries/stdlib/api/js/kotlin.io.encoding.kt
@@ -17,11 +17,29 @@
 
     public final fun encodeToByteArray(source: kotlin.ByteArray, startIndex: kotlin.Int = ..., endIndex: kotlin.Int = ...): kotlin.ByteArray
 
+    public final fun withPadding(onEncode: kotlin.io.encoding.Base64.EncodePadding = ..., onDecode: kotlin.io.encoding.Base64.DecodePadding = ...): kotlin.io.encoding.Base64
+
+    public final fun withPaddingOptions(encodeOption: kotlin.io.encoding.Base64.EncodePadding = ..., decodeOption: kotlin.io.encoding.Base64.DecodePadding = ...): kotlin.io.encoding.Base64
+
+    public final enum class DecodePadding : kotlin.Enum<kotlin.io.encoding.Base64.DecodePadding> {
+        enum entry REQUIRE_PRESENT
+
+        enum entry REQUIRE_ABSENT
+
+        enum entry ALLOW_BOTH
+    }
+
     public companion object of Base64 Default : kotlin.io.encoding.Base64 {
         public final val Mime: kotlin.io.encoding.Base64 { get; }
 
         public final val UrlSafe: kotlin.io.encoding.Base64 { get; }
     }
+
+    public final enum class EncodePadding : kotlin.Enum<kotlin.io.encoding.Base64.EncodePadding> {
+        enum entry WRITE
+
+        enum entry OMIT
+    }
 }
 
 @kotlin.RequiresOptIn(level = Level.ERROR)
diff --git a/libraries/stdlib/src/kotlin/io/encoding/Base64.kt b/libraries/stdlib/src/kotlin/io/encoding/Base64.kt
index 8f94169..4283db6 100644
--- a/libraries/stdlib/src/kotlin/io/encoding/Base64.kt
+++ b/libraries/stdlib/src/kotlin/io/encoding/Base64.kt
@@ -19,12 +19,51 @@
 @ExperimentalEncodingApi
 public open class Base64 private constructor(
     internal val isUrlSafe: Boolean,
-    internal val isMimeScheme: Boolean
+    internal val isMimeScheme: Boolean,
+    private val encodePadding: EncodePadding = EncodePadding.WRITE,
+    private val decodePadding: DecodePadding = DecodePadding.REQUIRE_PRESENT
 ) {
     init {
         require(!isUrlSafe || !isMimeScheme)
     }
 
+    public enum class EncodePadding {
+        WRITE,
+        OMIT
+    }
+
+    public enum class DecodePadding {
+        REQUIRE_PRESENT,
+        REQUIRE_ABSENT,
+        ALLOW_BOTH
+    }
+
+    // TODO: Having default values can implicitly change the option that was not specified.
+    //   Should the default value be null?
+    public fun withPadding(
+        onEncode: EncodePadding = EncodePadding.WRITE,
+        onDecode: DecodePadding = DecodePadding.REQUIRE_PRESENT
+    ): Base64 {
+        return if (onEncode != encodePadding || onDecode != decodePadding) {
+            Base64(isUrlSafe, isMimeScheme, onEncode, onDecode)
+        } else {
+            this
+        }
+    }
+
+    // TODO: Having default values can implicitly change the option that was not specified.
+    //   Should the default value be null?
+    public fun withPaddingOptions(
+        encodeOption: EncodePadding = EncodePadding.WRITE,
+        decodeOption: DecodePadding = DecodePadding.REQUIRE_PRESENT
+    ): Base64 {
+        return if (encodeOption != encodePadding || decodeOption != decodePadding) {
+            Base64(isUrlSafe, isMimeScheme, encodeOption, decodeOption)
+        } else {
+            this
+        }
+    }
+
     /**
      * Encodes bytes from the specified [source] array or its subrange.
      * Returns a [ByteArray] containing the resulting symbols.
@@ -301,8 +340,10 @@
                 val bits = byte1 shl 4
                 destination[destinationIndex++] = encodeMap[bits ushr 6]
                 destination[destinationIndex++] = encodeMap[bits and 0x3F]
-                destination[destinationIndex++] = padSymbol
-                destination[destinationIndex++] = padSymbol
+                if (encodePadding == EncodePadding.WRITE) {
+                    destination[destinationIndex++] = padSymbol
+                    destination[destinationIndex++] = padSymbol
+                }
             }
             2 -> {
                 val byte1 = source[sourceIndex++].toInt() and 0xFF
@@ -311,7 +352,9 @@
                 destination[destinationIndex++] = encodeMap[bits ushr 12]
                 destination[destinationIndex++] = encodeMap[(bits ushr 6) and 0x3F]
                 destination[destinationIndex++] = encodeMap[bits and 0x3F]
-                destination[destinationIndex++] = padSymbol
+                if (encodePadding == EncodePadding.WRITE) {
+                    destination[destinationIndex++] = padSymbol
+                }
             }
         }
 
@@ -324,7 +367,10 @@
         // includes padding chars
         val groups = (sourceSize + bytesPerGroup - 1) / bytesPerGroup
         val lineSeparators = if (isMimeScheme) (groups - 1) / mimeGroupsPerLine else 0
-        val size = groups * symbolsPerGroup + lineSeparators * 2
+        var size = groups * symbolsPerGroup + lineSeparators * 2
+        if (encodePadding == EncodePadding.OMIT) {
+            size -= (groups * bytesPerGroup - sourceSize) // 0, 1, or 2
+        }
         if (size < 0) { // Int overflow
             throw IllegalArgumentException("Input is too big")
         }
@@ -394,8 +440,12 @@
         if (byteStart == -bitsPerByte + bitsPerSymbol) { // dangling single symbol, incorrectly encoded
             throw IllegalArgumentException("The last unit of input does not have enough bits")
         }
-        if (byteStart != -bitsPerByte && !hasPadding) {
-            throw IllegalArgumentException("The input should be padded")
+        if (byteStart != -bitsPerByte) {
+            if (decodePadding == DecodePadding.REQUIRE_PRESENT && !hasPadding ||
+                decodePadding == DecodePadding.REQUIRE_ABSENT && hasPadding
+            ) {
+                throw IllegalArgumentException("The input should be padded")
+            }
         }
         if (payload != 0) { // the pad bits are non-zero
             throw IllegalArgumentException("The pad bits should be zeros")
diff --git a/libraries/stdlib/test/io.encoding/Base64Test.kt b/libraries/stdlib/test/io.encoding/Base64Test.kt
index 8696425..d82ac70 100644
--- a/libraries/stdlib/test/io.encoding/Base64Test.kt
+++ b/libraries/stdlib/test/io.encoding/Base64Test.kt
@@ -178,6 +178,23 @@
             // paddings are required
             assertFailsWith<IllegalArgumentException>(scheme) { codec.decode("Zg") }
             assertFailsWith<IllegalArgumentException>(scheme) { codec.decode("Zm9vYmE") }
+
+            // paddings are prohibited
+            val noPaddingCodec = codec.withPadding(onDecode = Base64.DecodePadding.REQUIRE_ABSENT)
+            assertFailsWith<IllegalArgumentException>(scheme) { noPaddingCodec.decode("Zg==") }
+            assertFailsWith<IllegalArgumentException>(scheme) { noPaddingCodec.decode("Zm9vYmE=") }
+
+            // paddings are allowed but not required
+            val allowPaddingCodec = codec.withPaddingOptions(decodeOption = Base64.DecodePadding.ALLOW_BOTH)
+            assertContentEquals("f".encodeToByteArray(), allowPaddingCodec.decode("Zg"), message = scheme)
+            assertContentEquals("fooba".encodeToByteArray(), allowPaddingCodec.decode("Zm9vYmE"), message = scheme)
+            assertContentEquals("f".encodeToByteArray(), allowPaddingCodec.decode("Zg=="), message = scheme)
+            assertContentEquals("fooba".encodeToByteArray(), allowPaddingCodec.decode("Zm9vYmE="), message = scheme)
+
+            // encode with no padding
+            val withoutPaddingBase64 = codec.withPadding(onEncode = Base64.EncodePadding.OMIT)
+            assertEquals("Zg", withoutPaddingBase64.encode("f".encodeToByteArray()))
+            assertEquals("Zm9vYmE", withoutPaddingBase64.encode("fooba".encodeToByteArray()))
         }
     }
 
@@ -272,4 +289,29 @@
         val expected = "Zm9vYmFy".repeat(76).chunked(76).joinToString(separator = "\r\n")
         testEncode(Base64.Mime, "foobar".repeat(76).encodeToByteArray(), expected)
     }
+}
+
+private fun f() {
+    val binary = "foobar".encodeToByteArray()
+    val text = "Zm9vYmFy".encodeToByteArray()
+
+    // Encode with padding
+    Base64.encode(binary)
+    Base64.Mime.encode(binary)
+
+    // Encode without padding
+    Base64.withPadding(onEncode = Base64.EncodePadding.WRITE).encode(binary)
+    Base64.UrlSafe.withPadding(onEncode = Base64.EncodePadding.WRITE).encode(binary)
+
+    // Decode with required padding
+    Base64.withPadding(onDecode = Base64.DecodePadding.REQUIRE_PRESENT).decode(text)
+    Base64.Mime.withPadding(onDecode = Base64.DecodePadding.REQUIRE_PRESENT).decode(text)
+
+    // Decode with required no padding
+    Base64.withPadding(onDecode = Base64.DecodePadding.REQUIRE_ABSENT).decode(text)
+    Base64.Mime.withPadding(onDecode = Base64.DecodePadding.REQUIRE_ABSENT).decode(text)
+
+    // Decode with required padding or no padding
+    Base64.withPadding(onDecode = Base64.DecodePadding.ALLOW_BOTH).decode(text)
+    Base64.Mime.withPadding(onDecode = Base64.DecodePadding.ALLOW_BOTH).decode(text)
 }
\ No newline at end of file
diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt
index 94f966a9..d706bd7 100644
--- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt
+++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt
@@ -3279,7 +3279,6 @@
 
 public class kotlin/io/encoding/Base64 {
 	public static final field Default Lkotlin/io/encoding/Base64$Default;
-	public synthetic fun <init> (ZZLkotlin/jvm/internal/DefaultConstructorMarker;)V
 	public final fun decode (Ljava/lang/CharSequence;II)[B
 	public final fun decode ([BII)[B
 	public static synthetic fun decode$default (Lkotlin/io/encoding/Base64;Ljava/lang/CharSequence;IIILjava/lang/Object;)[B
@@ -3296,6 +3295,19 @@
 	public static synthetic fun encodeToAppendable$default (Lkotlin/io/encoding/Base64;[BLjava/lang/Appendable;IIILjava/lang/Object;)Ljava/lang/Appendable;
 	public final fun encodeToByteArray ([BII)[B
 	public static synthetic fun encodeToByteArray$default (Lkotlin/io/encoding/Base64;[BIIILjava/lang/Object;)[B
+	public final fun withPadding (Lkotlin/io/encoding/Base64$EncodePadding;Lkotlin/io/encoding/Base64$DecodePadding;)Lkotlin/io/encoding/Base64;
+	public static synthetic fun withPadding$default (Lkotlin/io/encoding/Base64;Lkotlin/io/encoding/Base64$EncodePadding;Lkotlin/io/encoding/Base64$DecodePadding;ILjava/lang/Object;)Lkotlin/io/encoding/Base64;
+	public final fun withPaddingOptions (Lkotlin/io/encoding/Base64$EncodePadding;Lkotlin/io/encoding/Base64$DecodePadding;)Lkotlin/io/encoding/Base64;
+	public static synthetic fun withPaddingOptions$default (Lkotlin/io/encoding/Base64;Lkotlin/io/encoding/Base64$EncodePadding;Lkotlin/io/encoding/Base64$DecodePadding;ILjava/lang/Object;)Lkotlin/io/encoding/Base64;
+}
+
+public final class kotlin/io/encoding/Base64$DecodePadding : java/lang/Enum {
+	public static final field ALLOW_BOTH Lkotlin/io/encoding/Base64$DecodePadding;
+	public static final field REQUIRE_ABSENT Lkotlin/io/encoding/Base64$DecodePadding;
+	public static final field REQUIRE_PRESENT Lkotlin/io/encoding/Base64$DecodePadding;
+	public static fun getEntries ()Lkotlin/enums/EnumEntries;
+	public static fun valueOf (Ljava/lang/String;)Lkotlin/io/encoding/Base64$DecodePadding;
+	public static fun values ()[Lkotlin/io/encoding/Base64$DecodePadding;
 }
 
 public final class kotlin/io/encoding/Base64$Default : kotlin/io/encoding/Base64 {
@@ -3303,6 +3315,14 @@
 	public final fun getUrlSafe ()Lkotlin/io/encoding/Base64;
 }
 
+public final class kotlin/io/encoding/Base64$EncodePadding : java/lang/Enum {
+	public static final field OMIT Lkotlin/io/encoding/Base64$EncodePadding;
+	public static final field WRITE Lkotlin/io/encoding/Base64$EncodePadding;
+	public static fun getEntries ()Lkotlin/enums/EnumEntries;
+	public static fun valueOf (Ljava/lang/String;)Lkotlin/io/encoding/Base64$EncodePadding;
+	public static fun values ()[Lkotlin/io/encoding/Base64$EncodePadding;
+}
+
 public abstract interface annotation class kotlin/io/encoding/ExperimentalEncodingApi : java/lang/annotation/Annotation {
 }