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

base64.Encode.scala Maven / Gradle / Ivy

The newest version!
package base64

import scala.annotation.{switch, tailrec}

/** Base64 encodings. This implementation does not support line breaks */
object Encode {
  /** Encodes an array of bytes into a base64 encoded string
   *  which accounts for url encoding provisions */
  def urlSafe[T : Input](in: T, multiline: Boolean = false, pad: Boolean = true): Array[Byte] =
    encodeWith(URLSafeAlphabet)(in, multiline, pad)

 /** Encodes an array of bytes into a base64 encoded string
   *    */
  def apply[T : Input](in: T, multiline: Boolean = false, pad: Boolean = true): Array[Byte] =
    encodeWith(StdAlphabet)(in, multiline, pad)

  def encodeWith[T : Input](
    alphabet: Alphabet)
   (ins: T, multiline: Boolean = false, pad: Boolean = true): Array[Byte] = {
    val in = Input(ins)
    val index = alphabet.values
    val len = in.length
    val len2 = len - 2
    val estimate = (len / 3) * 4 + (if (len % 3 > 0) 4 else 0) match {
      case est => if (multiline) est + (est / MaxLine) else est
    }
    val out = new Array[Byte](estimate)

    @tailrec
    def write(d: Int = 0, e: Int = 0, col: Int = 0): (Int, Int) =
      if (d >= len2) (d, e)
      else {
        enc3to4(in, d, 3, out, e, index, pad)
        if (multiline && col + 4 >= MaxLine) {
          out.update(e + 4, NewLine)
          write(d + 3, e + 5, 0)
        } else write(d + 3, e + 4, col + 4)
      }

    val (d, e) = write()
    val fe = // extra padding
      if (d < len) {
        val updated = enc3to4(in, d, len - d, out, e, index, pad)
        e + updated
      } else e
    if (fe < out.length - 1) java.util.Arrays.copyOf(out, fe) else out
  }

  private def enc3to4(
    in: Array[Byte],
    inOffset: Int,
    numSigBytes: Int, 
    out: Array[Byte],
    outOffset: Int,
    index: IndexedSeq[Byte],
    pad: Boolean): Int = {

    //           1         2         3
    // 01234567890123456789012345678901 Bit position
    // --------000000001111111122222222 Array position from threeBytes
    // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
    //          >>18  >>12  >> 6  >> 0  Right shift necessary
    //                0x3f  0x3f  0x3f  Additional AND
  
    // Create buffer with zero-padding if there are only one or two
    // significant bytes passed in the array.
    // We have to shift left 24 in order to flush out the 1's that appear
    // when Java treats a value as negative that is cast from a byte to an int.
    val inBuff = (if (numSigBytes > 0) (in(inOffset)     << 24) >>> 8 else 0)  |
                 (if (numSigBytes > 1) (in(inOffset + 1) << 24) >>> 16 else 0) |
                 (if (numSigBytes > 2) (in(inOffset + 2) << 24) >>> 24 else 0)
    (numSigBytes: @switch) match {
      case 3 =>
        out.update(outOffset,     index(inBuff >>> 18))
        out.update(outOffset + 1, index(inBuff >>> 12 & EncMask))
        out.update(outOffset + 2, index(inBuff >>> 6 & EncMask))
        out.update(outOffset + 3, index(inBuff & EncMask))
        4
      case 2 =>
        out.update(outOffset,     index(inBuff >>> 18))
        out.update(outOffset + 1, index(inBuff >>> 12 & EncMask))
        out.update(outOffset + 2, index(inBuff >>> 6 & EncMask))
        if (pad) {
          out.update(outOffset + 3, Pad)
          4
        } else
          3
      case 1 =>
        out.update(outOffset,     index(inBuff >>> 18))
        out.update(outOffset + 1, index(inBuff >>> 12 & EncMask))
        if (pad) {
          out.update(outOffset + 2, Pad)
          out.update(outOffset + 3, Pad)
          4
        } else
          2
      case _ =>
        0
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy