diff options
author | Louis Pilfold <louis@lpil.uk> | 2021-09-07 20:05:33 +0100 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2021-09-07 20:05:33 +0100 |
commit | c5d415fdc206ae3403b2992c9a3e395188b45280 (patch) | |
tree | bdd136ad49863fb27749e06b0d568dce0314e9b5 | |
parent | f869915d92bf90c385ddaaa6c059b25784364ab0 (diff) | |
download | gleam_stdlib-c5d415fdc206ae3403b2992c9a3e395188b45280.tar.gz gleam_stdlib-c5d415fdc206ae3403b2992c9a3e395188b45280.zip |
base64 decode in JS
-rw-r--r-- | src/gleam/base.gleam | 37 | ||||
-rw-r--r-- | src/gleam_stdlib.js | 41 | ||||
-rw-r--r-- | test/gleam/base_test.gleam | 76 |
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)) } |