aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2021-09-07 20:05:33 +0100
committerLouis Pilfold <louis@lpil.uk>2021-09-07 20:05:33 +0100
commitc5d415fdc206ae3403b2992c9a3e395188b45280 (patch)
treebdd136ad49863fb27749e06b0d568dce0314e9b5
parentf869915d92bf90c385ddaaa6c059b25784364ab0 (diff)
downloadgleam_stdlib-c5d415fdc206ae3403b2992c9a3e395188b45280.tar.gz
gleam_stdlib-c5d415fdc206ae3403b2992c9a3e395188b45280.zip
base64 decode in JS
-rw-r--r--src/gleam/base.gleam37
-rw-r--r--src/gleam_stdlib.js41
-rw-r--r--test/gleam/base_test.gleam76
3 files changed, 97 insertions, 57 deletions
diff --git a/src/gleam/base.gleam b/src/gleam/base.gleam
index 0995f1b..6807767 100644
--- a/src/gleam/base.gleam
+++ b/src/gleam/base.gleam
@@ -20,18 +20,23 @@ if javascript {
"../gleam_stdlib.js" "encode64"
}
+/// Decodes a base 64 encoded string into a BitString.
+pub fn decode64(encoded: String) -> Result(BitString, Nil) {
+ let padded = case bit_string.byte_size(bit_string.from_string(encoded)) % 4 {
+ 0 -> encoded
+ n -> string.append(encoded, string.repeat("=", 4 - n))
+ }
+ do_decode64(padded)
+}
+
if erlang {
- external fn erl_decode64(String) -> Result(BitString, Nil) =
+ external fn do_decode64(String) -> Result(BitString, Nil) =
"gleam_stdlib" "base_decode64"
+}
- /// Decodes a base 64 encoded string into a BitString.
- pub fn decode64(encoded: String) -> Result(BitString, Nil) {
- let padded = case bit_string.byte_size(bit_string.from_string(encoded)) % 4 {
- 0 -> encoded
- n -> string.append(encoded, string.repeat("=", 4 - n))
- }
- erl_decode64(padded)
- }
+if javascript {
+ external fn do_decode64(String) -> Result(BitString, Nil) =
+ "../gleam_stdlib.js" "decode64"
}
/// Encodes a BitString into a base 64 encoded string with URL and filename safe alphabet.
@@ -41,12 +46,10 @@ pub fn url_encode64(input: BitString, padding: Bool) -> String {
|> string.replace("/", "_")
}
-if erlang {
- /// Decodes a base 64 encoded string with URL and filename safe alphabet into a BitString.
- pub fn url_decode64(encoded: String) -> Result(BitString, Nil) {
- encoded
- |> string.replace("-", "+")
- |> string.replace("_", "/")
- |> decode64()
- }
+/// Decodes a base 64 encoded string with URL and filename safe alphabet into a BitString.
+pub fn url_decode64(encoded: String) -> Result(BitString, Nil) {
+ encoded
+ |> string.replace("-", "+")
+ |> string.replace("_", "/")
+ |> decode64()
}
diff --git a/src/gleam_stdlib.js b/src/gleam_stdlib.js
index d26a4b6..bb63cbb 100644
--- a/src/gleam_stdlib.js
+++ b/src/gleam_stdlib.js
@@ -426,3 +426,44 @@ function uint6ToB64(nUint6) {
? 47
: 65;
}
+
+// From https://developer.mozilla.org/en-US/docs/Glossary/Base64#Solution_2_%E2%80%93_rewrite_the_DOMs_atob()_and_btoa()_using_JavaScript's_TypedArrays_and_UTF-8
+function b64ToUint6(nChr) {
+ return nChr > 64 && nChr < 91
+ ? nChr - 65
+ : nChr > 96 && nChr < 123
+ ? nChr - 71
+ : nChr > 47 && nChr < 58
+ ? nChr + 4
+ : nChr === 43
+ ? 62
+ : nChr === 47
+ ? 63
+ : 0;
+}
+
+// From https://developer.mozilla.org/en-US/docs/Glossary/Base64#Solution_2_%E2%80%93_rewrite_the_DOMs_atob()_and_btoa()_using_JavaScript's_TypedArrays_and_UTF-8
+export function decode64(sBase64) {
+ if (sBase64.match(/[^A-Za-z0-9\+\/=]/g)) return new Error(Nil);
+ let sB64Enc = sBase64.replace(/=/g, "");
+ let nInLen = sB64Enc.length;
+ let nOutLen = (nInLen * 3 + 1) >> 2;
+ let taBytes = new Uint8Array(nOutLen);
+
+ for (
+ let nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0;
+ nInIdx < nInLen;
+ nInIdx++
+ ) {
+ nMod4 = nInIdx & 3;
+ nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << (6 * (3 - nMod4));
+ if (nMod4 === 3 || nInLen - nInIdx === 1) {
+ for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
+ taBytes[nOutIdx] = (nUint24 >>> ((16 >>> nMod3) & 24)) & 255;
+ }
+ nUint24 = 0;
+ }
+ }
+
+ return new Ok(new BitString(taBytes));
+}
diff --git a/test/gleam/base_test.gleam b/test/gleam/base_test.gleam
index ade55c8..c783efa 100644
--- a/test/gleam/base_test.gleam
+++ b/test/gleam/base_test.gleam
@@ -20,28 +20,26 @@ pub fn encode64_test() {
|> should.equal("")
}
-if erlang {
- pub fn decode64_test() {
- "/3/+/A=="
- |> base.decode64()
- |> should.equal(Ok(<<255, 127, 254, 252>>))
-
- "/3/+/A"
- |> base.decode64()
- |> should.equal(Ok(<<255, 127, 254, 252>>))
-
- "AAAA"
- |> base.decode64()
- |> should.equal(Ok(<<0, 0, 0>>))
-
- ""
- |> base.decode64()
- |> should.equal(Ok(<<>>))
-
- ")!"
- |> base.decode64()
- |> should.equal(Error(Nil))
- }
+pub fn decode64_test() {
+ "/3/+/A=="
+ |> base.decode64()
+ |> should.equal(Ok(<<255, 127, 254, 252>>))
+
+ "/3/+/A"
+ |> base.decode64()
+ |> should.equal(Ok(<<255, 127, 254, 252>>))
+
+ "AAAA"
+ |> base.decode64()
+ |> should.equal(Ok(<<0, 0, 0>>))
+
+ ""
+ |> base.decode64()
+ |> should.equal(Ok(<<>>))
+
+ ")!"
+ |> base.decode64()
+ |> should.equal(Error(Nil))
}
pub fn url_encode64_test() {
@@ -62,26 +60,24 @@ pub fn url_encode64_test() {
|> should.equal("")
}
-if erlang {
- pub fn url_decode64_test() {
- "_3_-_A=="
- |> base.url_decode64()
- |> should.equal(Ok(<<255, 127, 254, 252>>))
+pub fn url_decode64_test() {
+ "_3_-_A=="
+ |> base.url_decode64()
+ |> should.equal(Ok(<<255, 127, 254, 252>>))
- "_3_-_A"
- |> base.url_decode64()
- |> should.equal(Ok(<<255, 127, 254, 252>>))
+ "_3_-_A"
+ |> base.url_decode64()
+ |> should.equal(Ok(<<255, 127, 254, 252>>))
- "AAAA"
- |> base.url_decode64()
- |> should.equal(Ok(<<0, 0, 0>>))
+ "AAAA"
+ |> base.url_decode64()
+ |> should.equal(Ok(<<0, 0, 0>>))
- ""
- |> base.url_decode64()
- |> should.equal(Ok(<<>>))
+ ""
+ |> base.url_decode64()
+ |> should.equal(Ok(<<>>))
- ")!"
- |> base.url_decode64()
- |> should.equal(Error(Nil))
- }
+ ")!"
+ |> base.url_decode64()
+ |> should.equal(Error(Nil))
}