Skip to content

Instantly share code, notes, and snippets.

@ykon
Last active October 25, 2017 13:37
Show Gist options
  • Save ykon/93b0fd6333dc45867576847526a751ec to your computer and use it in GitHub Desktop.
Save ykon/93b0fd6333dc45867576847526a751ec to your computer and use it in GitHub Desktop.
Base64 in Scala
/*
* Copyright (c) 2017 Yuki Ono
* Licensed under the MIT License.
*/
package func_base64
// https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64
object Base64 extends App {
private val base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
def proceduralEncode(input: String): String = {
var s = input
var c = s.length % 3
var pad = ""
if (c > 0) {
while (c < 3) {
pad += "="
s += "\0"
c += 1
}
}
val res = new StringBuffer()
c = 0
while (c < s.length) {
if (c > 0 && (c / 3 * 4) % 76 == 0)
res.append("\r\n")
val n = (s.charAt(c) << 16) | (s.charAt(c + 1) << 8) | (s.charAt(c + 2));
val n1 = (n >>> 18) & 63
val n2 = (n >>> 12) & 63
val n3 = (n >>> 6) & 63
val n4 = n & 63
res.append(base64chars.charAt(n1))
res.append(base64chars.charAt(n2))
res.append(base64chars.charAt(n3))
res.append(base64chars.charAt(n4))
c += 3
}
res.substring(0, res.length - pad.length) + pad
}
def fpEncode(input: String): String = {
def getPadLen(s: String) = {
val c = input.length % 3
if (c > 0) 3 - c else 0
}
val pad = "=" * getPadLen(input)
val pstr = input + ("\0" * pad.length)
def to24bit(s: String) = {
val cs = Array(0, 1, 2).map(s.charAt)
cs(0) << 16 | cs(1) << 8 | cs(2)
}
def toFour6bit(n: Int) =
Array(n >>> 18, n >>> 12, n >>> 6, n).map(_ & 63)
pstr.grouped(3).map(to24bit).flatMap(toFour6bit).map(base64chars.charAt)
.mkString.grouped(76).mkString("\r\n").dropRight(pad.length) + pad
}
println(proceduralEncode("test"))
println(fpEncode("test"))
println(proceduralEncode("base64"))
println(fpEncode("base64"))
val longText = "Base64 is a group of similar binary-to-text encoding schemes that represent binary data in an ASCII string format by translating it into a radix-64 representation. The term Base64 originates from a specific MIME content transfer encoding."
println(proceduralEncode(longText))
println(fpEncode(longText))
def proceduralDecode(input: String): String = {
var s = input.replaceAll("[^" + base64chars + "=]", "")
val pad = if (s.charAt(s.length() - 1) == '=')
(if(s.charAt(s.length() - 2) == '=') "AA" else "A") else ""
s = s.substring(0, s.length() - pad.length()) + pad
val res = new StringBuffer()
var c = 0
while (c < s.length()) {
val n = (base64chars.indexOf(s.charAt(c)) << 18) |
(base64chars.indexOf(s.charAt(c + 1)) << 12) |
(base64chars.indexOf(s.charAt(c + 2)) << 6) |
base64chars.indexOf(s.charAt(c + 3))
res.append(((n >>> 16) & 0xFF).asInstanceOf[Char])
res.append(((n >>> 8) & 0xFF).asInstanceOf[Char])
res.append((n & 0xFF).asInstanceOf[Char])
c += 4
}
return res.substring(0, res.length() - pad.length());
}
def fpDecode(input: String): String = {
val line = input.replaceAll("[^" + base64chars + "=]", "")
val padLen = line.count(_ == '=')
val pstr = line.replace('=', 'A');
def to24bit(s: String) = {
val cs = Array(0, 1, 2, 3).map(s.charAt).map(base64chars.indexOf(_))
cs(0) << 18 | cs(1) << 12 | cs(2) << 6 | cs(3)
}
def to8bitChar(n: Int) = (n & 0xFF).asInstanceOf[Char]
def toThree8bit(n: Int) = Array(n >>> 16, n >>> 8, n).map(to8bitChar)
pstr.grouped(4).map(to24bit).flatMap(toThree8bit)
.mkString.dropRight(padLen)
}
println(proceduralDecode("dGVzdA=="))
println(fpDecode("dGVzdA=="))
println(proceduralDecode("YmFzZTY0"))
println(fpDecode("YmFzZTY0"))
val longBase64 = """
QmFzZTY0IGlzIGEgZ3JvdXAgb2Ygc2ltaWxhciBiaW5hcnktdG8tdGV4dCBlbmNvZGluZyBzY2hl
bWVzIHRoYXQgcmVwcmVzZW50IGJpbmFyeSBkYXRhIGluIGFuIEFTQ0lJIHN0cmluZyBmb3JtYXQg
YnkgdHJhbnNsYXRpbmcgaXQgaW50byBhIHJhZGl4LTY0IHJlcHJlc2VudGF0aW9uLiBUaGUgdGVy
bSBCYXNlNjQgb3JpZ2luYXRlcyBmcm9tIGEgc3BlY2lmaWMgTUlNRSBjb250ZW50IHRyYW5zZmVy
IGVuY29kaW5nLg==
"""
println(proceduralDecode(longBase64))
println(fpDecode(longBase64))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment