Skip to content

Instantly share code, notes, and snippets.

@MartinDevi
Created May 23, 2019 12:25
Show Gist options
  • Save MartinDevi/3cc502319ea3ffea94c873259ef2f60d to your computer and use it in GitHub Desktop.
Save MartinDevi/3cc502319ea3ffea94c873259ef2f60d to your computer and use it in GitHub Desktop.
Base64 Decoder Source for Okio
package okio
import java.io.Reader
class Base64Decoder(
private val reader: Reader,
private val timeout: Timeout = Timeout()
) : Source {
private var charBufferOffset = 0
private var charBufferLength = 0
private val charBuffer = CharArray(CHAR_BUFFER_SIZE)
private var byteBufferOffset = 0
private var byteBufferLength = 0
private val byteBuffer = ByteArray(3)
override fun read(sink: Buffer, byteCount: Long): Long {
if (byteCount == 0L) return 0
require(byteCount >= 0) { "byteCount < 0: $byteCount" }
timeout.throwIfReached()
val maxCount = minOf(byteCount, CHAR_BUFFER_SIZE / 4 * 3).toInt()
if (byteBufferLength > 0) {
// Read from byte buffer.
val count = minOf(byteBufferLength, maxCount)
sink.write(byteBuffer, byteBufferOffset, count)
byteBufferOffset += count
byteBufferLength -= count
return count.toLong()
}
if (charBufferLength >= 4) {
// Decode from char buffer.
return if (byteCount < 3) {
// Decode next block into byte buffer, then read only from it into sink.
charBuffer.decodeBase64(charBufferOffset, 4, byteBuffer)
sink.write(byteBuffer, 0, maxCount)
charBufferOffset += 4
charBufferLength -= 4
byteBufferOffset = maxCount
byteBufferLength = 3 - maxCount
byteCount
} else {
// Decode as many complete blocks as possible directly into sink.
val count = minOf(charBufferLength / 4 * 3, maxCount - byteCount.rem(3).toInt())
val charCount = count / 3 * 4
charBuffer.decodeBase64(charBufferOffset, charCount, sink)
charBufferOffset += charCount
charBufferLength -= charCount
count.toLong()
}
}
// Shift buffer contents to its start, to make sure that there is enough space after.
charBuffer.copyInto(charBuffer, 0, charBufferOffset, charBufferOffset + charBufferLength)
while (charBufferLength < 4) {
// Read into char buffer until at least a full block is available or the stream is entirely consumed.
val read = reader.read(charBuffer, charBufferLength, charBuffer.size - charBufferLength)
if (read == -1) {
if (charBufferLength == 0) {
return -1
} else {
throw Exception("Malformed Base64")
}
}
charBufferLength += read
}
// Decode as many complete blocks as possible directly into sink.
val count = minOf(charBufferLength / 4 * 3, maxCount)
val charCount = count / 3 * 4
charBuffer.decodeBase64(0, charCount, sink)
charBufferOffset = charCount
charBufferLength -= charCount
return count.toLong()
}
override fun timeout(): Timeout =
timeout
override fun close() =
reader.close()
companion object {
const val CHAR_BUFFER_SIZE = 1024
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment