All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.okio.-Base64.kt Maven / Gradle / Ivy

There is a newer version: 4.0.0
Show newest version
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package okio

import okio.ByteString.Companion.encodeUtf8
import kotlin.native.concurrent.SharedImmutable

/** @author Alexander Y. Kleymenov */

@SharedImmutable
internal val BASE64 =
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".encodeUtf8().data
@SharedImmutable
internal val BASE64_URL_SAFE =
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".encodeUtf8().data

internal fun String.decodeBase64ToArray(): ByteArray? {
  // Ignore trailing '=' padding and whitespace from the input.
  var limit = length
  while (limit > 0) {
    val c = this[limit - 1]
    if (c != '=' && c != '\n' && c != '\r' && c != ' ' && c != '\t') {
      break
    }
    limit--
  }

  // If the input includes whitespace, this output array will be longer than necessary.
  val out = ByteArray((limit * 6L / 8L).toInt())
  var outCount = 0
  var inCount = 0

  var word = 0
  for (pos in 0 until limit) {
    val c = this[pos]

    val bits: Int
    if (c in 'A'..'Z') {
      // char ASCII value
      //  A    65    0
      //  Z    90    25 (ASCII - 65)
      bits = c.code - 65
    } else if (c in 'a'..'z') {
      // char ASCII value
      //  a    97    26
      //  z    122   51 (ASCII - 71)
      bits = c.code - 71
    } else if (c in '0'..'9') {
      // char ASCII value
      //  0    48    52
      //  9    57    61 (ASCII + 4)
      bits = c.code + 4
    } else if (c == '+' || c == '-') {
      bits = 62
    } else if (c == '/' || c == '_') {
      bits = 63
    } else if (c == '\n' || c == '\r' || c == ' ' || c == '\t') {
      continue
    } else {
      return null
    }

    // Append this char's 6 bits to the word.
    word = word shl 6 or bits

    // For every 4 chars of input, we accumulate 24 bits of output. Emit 3 bytes.
    inCount++
    if (inCount % 4 == 0) {
      out[outCount++] = (word shr 16).toByte()
      out[outCount++] = (word shr 8).toByte()
      out[outCount++] = word.toByte()
    }
  }

  val lastWordChars = inCount % 4
  when (lastWordChars) {
    1 -> {
      // We read 1 char followed by "===". But 6 bits is a truncated byte! Fail.
      return null
    }
    2 -> {
      // We read 2 chars followed by "==". Emit 1 byte with 8 of those 12 bits.
      word = word shl 12
      out[outCount++] = (word shr 16).toByte()
    }
    3 -> {
      // We read 3 chars, followed by "=". Emit 2 bytes for 16 of those 18 bits.
      word = word shl 6
      out[outCount++] = (word shr 16).toByte()
      out[outCount++] = (word shr 8).toByte()
    }
  }

  // If we sized our out array perfectly, we're done.
  if (outCount == out.size) return out

  // Copy the decoded bytes to a new, right-sized array.
  return out.copyOf(outCount)
}

internal fun ByteArray.encodeBase64(map: ByteArray = BASE64): String {
  val length = (size + 2) / 3 * 4
  val out = ByteArray(length)
  var index = 0
  val end = size - size % 3
  var i = 0
  while (i < end) {
    val b0 = this[i++].toInt()
    val b1 = this[i++].toInt()
    val b2 = this[i++].toInt()
    out[index++] = map[(b0 and 0xff shr 2)]
    out[index++] = map[(b0 and 0x03 shl 4) or (b1 and 0xff shr 4)]
    out[index++] = map[(b1 and 0x0f shl 2) or (b2 and 0xff shr 6)]
    out[index++] = map[(b2 and 0x3f)]
  }
  when (size - end) {
    1 -> {
      val b0 = this[i].toInt()
      out[index++] = map[b0 and 0xff shr 2]
      out[index++] = map[b0 and 0x03 shl 4]
      out[index++] = '='.code.toByte()
      out[index] = '='.code.toByte()
    }
    2 -> {
      val b0 = this[i++].toInt()
      val b1 = this[i].toInt()
      out[index++] = map[(b0 and 0xff shr 2)]
      out[index++] = map[(b0 and 0x03 shl 4) or (b1 and 0xff shr 4)]
      out[index++] = map[(b1 and 0x0f shl 2)]
      out[index] = '='.code.toByte()
    }
  }
  return out.toUtf8String()
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy