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

base64.Decode.scala Maven / Gradle / Ivy

The newest version!
package base64

object Decode {
  val Empty = Array.empty[Byte]

  sealed trait Failure
  case class InvalidByte(index: Int, dec: Int) extends Failure

  def urlSafe[T : Input](in: T): Either[Failure, Array[Byte]] =
    decodeWith(URLSafeAlphabet)(in)

  def apply[T : Input](in: T): Either[Failure, Array[Byte]] =
    decodeWith(StdAlphabet)(in)

  def decodeWith[T : Input](
    alphabet: Alphabet)(ins: T): Either[Failure, Array[Byte]] = {
    val in = Input(ins) match {
      case in if in.length % 4 == 0 =>
        in
      case p =>
        def concat(a: Array[Byte], b: Array[Byte]): Array[Byte] = {
          val res = new Array[Byte](a.length + b.length)
          System.arraycopy(a, 0, res, 0, a.length)
          System.arraycopy(b, 0, res, a.length, b.length)
          res
        }
        // if padding was omitted, fill it in ourselves
        concat(p, Array.fill(p.length % 4)(Pad))
    }
    val len = in.length
    val len34 = len * 3 / 4
    val out = new Array[Byte](len34)
    val b4 = new Array[Byte](4)
    val index = alphabet.reversed
    val readBounds = len

    def read(
      at: Int = 0,
      b4Posn: Int = 0,
      outOffset: Int = 0
    ): Either[Failure, Int] = if (at >= readBounds) Right(outOffset) else {
      val sbiCrop = (in(at) & 0x7f).toByte  // Only the low seven bits
      val sbiDecode = index(sbiCrop)
      val nextByte = at + 1
      if (sbiDecode >= WhiteSpaceEnc) {
        if (sbiDecode >= EqEnc) {
          b4.update(b4Posn, sbiCrop)
          val nextB4Posn = b4Posn + 1
          if (nextB4Posn > 3) {
            val cnt = dec4to3(
              b4, 0, out, outOffset, index
            )
            val curOffset = outOffset + cnt
            if (sbiCrop == Pad) Right(curOffset) else read(
              nextByte, 0, curOffset
            )
          } else read(nextByte, nextB4Posn, outOffset)
        } else read(nextByte, b4Posn, outOffset)
      } else Left(InvalidByte(at, index(at) & 0xFF))
    }
    if (len < 4) Right(Empty) else read().right.map { len =>
      if (len == 1 && out(0) == -1) /*all padding*/ Empty
      else java.util.Arrays.copyOf(out, len)
    }
  }

  private def dec4to3(
    in: Array[Byte],
    inOffset: Int,
    out: Array[Byte],
    outOffset: Int,
    index: Array[Byte]
  ): Int =
    if (in(inOffset + 2) == Pad) { // Dk==
      val outBuff = ((index(in(inOffset)) & 0xFF)      << 18) |
                    ((index(in(inOffset + 1)) & 0xFF ) << 12)
      out.update(outOffset, (outBuff >>> 16).toByte)
      1
    } else if (in(inOffset + 3) == Pad) { // DkL=
      val outBuff = ((index(in(inOffset)) & 0xFF)     << 18) |
                    ((index(in(inOffset + 1)) & 0xFF) << 12) |
                    ((index(in(inOffset + 2)) & 0xFF) <<  6)
      out.update(outOffset, (outBuff >>> 16).toByte)
      out.update(outOffset + 1, (outBuff >>> 8).toByte)
      2
    } else { // DkLE
      val outBuff = ((index(in(inOffset)) & 0xFF)     << 18) |
                    ((index(in(inOffset + 1)) & 0xFF) << 12) |
                    ((index(in(inOffset + 2)) & 0xFF) << 6)  |
                    (index(in(inOffset + 3)) & 0xFF)
      out.update(outOffset, (outBuff >> 16).toByte)
      out.update(outOffset + 1, (outBuff >> 8).toByte)
      out.update(outOffset + 2, outBuff.toByte)
      3
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy