-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
366 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/* | ||
* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. | ||
*/ | ||
|
||
package io.ktor.utils.io.charsets | ||
|
||
import org.khronos.webgl.* | ||
|
||
internal fun Decoder(encoding: String, fatal: Boolean = true): Decoder = try { | ||
TextDecoder(encoding, textDecoderOptions(fatal)).toKtor() | ||
} catch (cause: Throwable) { | ||
TextDecoderFallback(encoding, fatal) | ||
} | ||
|
||
internal interface Decoder { | ||
fun decode(): String | ||
fun decode(buffer: ArrayBufferView): String | ||
fun decode(buffer: ArrayBufferView, options: dynamic): String | ||
} | ||
|
||
@Suppress("NOTHING_TO_INLINE") | ||
internal inline fun Decoder.decodeStream(buffer: ArrayBufferView, stream: Boolean): String { | ||
decodeWrap { | ||
return decode(buffer, decodeOptions(stream)) | ||
} | ||
} | ||
|
||
internal fun decodeOptions(stream: Boolean): dynamic = Any().apply { | ||
with(this.asDynamic()) { | ||
this.stream = stream | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
ktor-io/js/src/io/ktor/utils/io/charsets/TextDecoder.js.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* | ||
* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. | ||
*/ | ||
|
||
package io.ktor.utils.io.charsets | ||
|
||
import org.khronos.webgl.* | ||
|
||
internal external class TextDecoder(encoding: String, options: dynamic = definedExternally) { | ||
val encoding: String | ||
|
||
fun decode(): String | ||
fun decode(buffer: ArrayBuffer): String | ||
fun decode(buffer: ArrayBuffer, options: dynamic): String | ||
fun decode(buffer: ArrayBufferView): String | ||
fun decode(buffer: ArrayBufferView, options: dynamic): String | ||
} | ||
|
||
internal fun TextDecoder.toKtor(): Decoder = object : Decoder { | ||
override fun decode(): String = this@toKtor.decode() | ||
override fun decode(buffer: ArrayBufferView): String = this@toKtor.decode(buffer) | ||
override fun decode(buffer: ArrayBufferView, options: dynamic): String = this@toKtor.decode(buffer, options) | ||
} | ||
|
||
internal fun textDecoderOptions(fatal: Boolean = false): Any = Any().apply { | ||
with(this.asDynamic()) { | ||
this.fatal = fatal | ||
} | ||
} |
83 changes: 83 additions & 0 deletions
83
ktor-io/js/src/io/ktor/utils/io/charsets/TextDecoderFallback.js.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/* | ||
* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. | ||
*/ | ||
|
||
package io.ktor.utils.io.charsets | ||
|
||
import io.ktor.utils.io.core.* | ||
import org.khronos.webgl.* | ||
|
||
private val ENCODING_ALIASES = setOf( | ||
"ansi_x3.4-1968", | ||
"ascii", | ||
"cp1252", | ||
"cp819", | ||
"csisolatin1", | ||
"ibm819", | ||
"iso-8859-1", | ||
"iso-ir-100", | ||
"iso8859-1", | ||
"iso88591", | ||
"iso_8859-1", | ||
"iso_8859-1:1987", | ||
"l1", | ||
"latin1", | ||
"us-ascii", | ||
"windows-1252", | ||
"x-cp1252" | ||
) | ||
|
||
private val REPLACEMENT = byteArrayOf(0xEF.toByte(), 0xBF.toByte(), 0xBD.toByte()) | ||
|
||
/** | ||
* Windows-1252 decoder. | ||
* | ||
* According to https://encoding.spec.whatwg.org/, ISO-8859-1 should be treated as windows-1252 for http. | ||
*/ | ||
internal class TextDecoderFallback( | ||
encoding: String, | ||
val fatal: Boolean | ||
) : Decoder { | ||
|
||
init { | ||
val requestedEncoding = encoding.trim().lowercase() | ||
check(ENCODING_ALIASES.contains(requestedEncoding)) { "$encoding is not supported." } | ||
} | ||
|
||
override fun decode(): String = "" | ||
|
||
override fun decode(buffer: ArrayBufferView): String = buildPacket { | ||
val bytes = buffer as Int8Array | ||
for (index in 0 until bytes.length) { | ||
val byte = bytes[index] | ||
val point: Int = byte.toCodePoint() | ||
|
||
if (point < 0) { | ||
check(!fatal) { "Invalid character: $point" } | ||
writeFully(REPLACEMENT) | ||
continue | ||
} | ||
|
||
if (point > 0xFF) { | ||
writeByte((point shr 8).toByte()) | ||
} | ||
|
||
writeByte((point and 0xFF).toByte()) | ||
} | ||
}.readBytes().decodeToString() | ||
|
||
override fun decode(buffer: ArrayBufferView, options: dynamic): String { | ||
return decode(buffer) | ||
} | ||
} | ||
|
||
private fun Byte.toCodePoint(): Int { | ||
val value = toInt() and 0xFF | ||
if (value.isASCII()) { | ||
return value | ||
} | ||
|
||
return WIN1252_TABLE[value - 0x80] | ||
} | ||
|
||
private fun Int.isASCII(): Boolean = this in 0..0x7F |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package io.ktor.utils.io.js | ||
|
||
import org.khronos.webgl.* | ||
|
||
internal external class TextEncoder() { | ||
val encoding: String | ||
|
||
public fun encode(input: String): Uint8Array | ||
} |
13 changes: 13 additions & 0 deletions
13
ktor-io/jsAndWasmShared/src/io/ktor/utils/io/charsets/DecodeUtils.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/* | ||
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. | ||
*/ | ||
|
||
package io.ktor.utils.io.charsets | ||
|
||
internal inline fun <R> decodeWrap(block: () -> R): R { | ||
try { | ||
return block() | ||
} catch (cause: Throwable) { | ||
throw MalformedInputException("Failed to decode bytes: ${cause.message ?: "no cause provided"}") | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
ktor-io/jsAndWasmShared/src/io/ktor/utils/io/charsets/ISO88591.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package io.ktor.utils.io.charsets | ||
|
||
import kotlinx.io.* | ||
|
||
internal fun encodeISO88591(input: CharSequence, fromIndex: Int, toIndex: Int, dst: Sink): Int { | ||
if (fromIndex >= toIndex) return 0 | ||
|
||
for (index in fromIndex until toIndex) { | ||
val character = input[index].code | ||
if (character > 0xff) { | ||
failedToMapError(character) | ||
} | ||
dst.writeByte(character.toByte()) | ||
} | ||
return toIndex - fromIndex | ||
} | ||
|
||
private fun failedToMapError(ch: Int): Nothing { | ||
throw MalformedInputException("The character with unicode point $ch couldn't be mapped to ISO-8859-1 character") | ||
} |
Oops, something went wrong.