diff options
author | Louis Pilfold <louis@lpil.uk> | 2021-09-07 19:53:09 +0100 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2021-09-07 19:53:09 +0100 |
commit | f869915d92bf90c385ddaaa6c059b25784364ab0 (patch) | |
tree | c348709049004310d94f5160bc16eb19a05a44de | |
parent | 43dad5dbfd3ef19257cbf8e781b0e8e0697b6b95 (diff) | |
download | gleam_stdlib-f869915d92bf90c385ddaaa6c059b25784364ab0.tar.gz gleam_stdlib-f869915d92bf90c385ddaaa6c059b25784364ab0.zip |
base64 encode in JS
-rw-r--r-- | src/gleam/base.gleam | 47 | ||||
-rw-r--r-- | src/gleam_stdlib.js | 44 | ||||
-rw-r--r-- | test/gleam/base_test.gleam | 75 |
3 files changed, 110 insertions, 56 deletions
diff --git a/src/gleam/base.gleam b/src/gleam/base.gleam index b589cb6..0995f1b 100644 --- a/src/gleam/base.gleam +++ b/src/gleam/base.gleam @@ -1,22 +1,29 @@ -if erlang { - import gleam/bit_string - import gleam/string +import gleam/bit_string +import gleam/string + +/// Encodes a BitString into a base 64 encoded string. +pub fn encode64(input: BitString, padding: Bool) -> String { + let encoded = do_encode64(input) + case padding { + True -> encoded + False -> string.replace(encoded, "=", "") + } +} - external fn erl_encode64(BitString) -> String = +if erlang { + external fn do_encode64(BitString) -> String = "base64" "encode" +} + +if javascript { + external fn do_encode64(BitString) -> String = + "../gleam_stdlib.js" "encode64" +} +if erlang { external fn erl_decode64(String) -> Result(BitString, Nil) = "gleam_stdlib" "base_decode64" - /// Encodes a BitString into a base 64 encoded string. - pub fn encode64(input: BitString, padding: Bool) -> String { - let encoded = erl_encode64(input) - case padding { - True -> encoded - False -> string.replace(encoded, "=", "") - } - } - /// 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 { @@ -25,14 +32,16 @@ if erlang { } erl_decode64(padded) } +} - /// Encodes a BitString into a base 64 encoded string with URL and filename safe alphabet. - pub fn url_encode64(input: BitString, padding: Bool) -> String { - encode64(input, padding) - |> string.replace("+", "-") - |> string.replace("/", "_") - } +/// Encodes a BitString into a base 64 encoded string with URL and filename safe alphabet. +pub fn url_encode64(input: BitString, padding: Bool) -> String { + encode64(input, padding) + |> string.replace("+", "-") + |> 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 diff --git a/src/gleam_stdlib.js b/src/gleam_stdlib.js index 3658142..d26a4b6 100644 --- a/src/gleam_stdlib.js +++ b/src/gleam_stdlib.js @@ -382,3 +382,47 @@ export function parse_query(query) { return new Error(Nil); } } + +// 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 encode64(bit_string) { + let aBytes = bit_string.buffer; + let nMod3 = 2, + sB64Enc = ""; + + for (let nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) { + nMod3 = nIdx % 3; + if (nIdx > 0 && ((nIdx * 4) / 3) % 76 === 0) { + sB64Enc += "\r\n"; + } + nUint24 |= aBytes[nIdx] << ((16 >>> nMod3) & 24); + if (nMod3 === 2 || aBytes.length - nIdx === 1) { + sB64Enc += String.fromCharCode( + uint6ToB64((nUint24 >>> 18) & 63), + uint6ToB64((nUint24 >>> 12) & 63), + uint6ToB64((nUint24 >>> 6) & 63), + uint6ToB64(nUint24 & 63) + ); + nUint24 = 0; + } + } + + return ( + sB64Enc.substr(0, sB64Enc.length - 2 + nMod3) + + (nMod3 === 2 ? "" : nMod3 === 1 ? "=" : "==") + ); +} + +// 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 uint6ToB64(nUint6) { + return nUint6 < 26 + ? nUint6 + 65 + : nUint6 < 52 + ? nUint6 + 71 + : nUint6 < 62 + ? nUint6 - 4 + : nUint6 === 62 + ? 43 + : nUint6 === 63 + ? 47 + : 65; +} diff --git a/test/gleam/base_test.gleam b/test/gleam/base_test.gleam index dd59196..ade55c8 100644 --- a/test/gleam/base_test.gleam +++ b/test/gleam/base_test.gleam @@ -1,27 +1,26 @@ -if erlang { - import gleam/base - import gleam/io - import gleam/list - import gleam/should - - pub fn encode64_test() { - <<255, 127, 254, 252>> - |> base.encode64(True) - |> should.equal("/3/+/A==") - - <<255, 127, 254, 252>> - |> base.encode64(False) - |> should.equal("/3/+/A") - - <<0, 0, 0>> - |> base.encode64(True) - |> should.equal("AAAA") - - <<>> - |> base.encode64(True) - |> should.equal("") - } +import gleam/base +import gleam/list +import gleam/should + +pub fn encode64_test() { + <<255, 127, 254, 252>> + |> base.encode64(True) + |> should.equal("/3/+/A==") + + <<255, 127, 254, 252>> + |> base.encode64(False) + |> should.equal("/3/+/A") + + <<0, 0, 0>> + |> base.encode64(True) + |> should.equal("AAAA") + + <<>> + |> base.encode64(True) + |> should.equal("") +} +if erlang { pub fn decode64_test() { "/3/+/A==" |> base.decode64() @@ -43,25 +42,27 @@ if erlang { |> base.decode64() |> should.equal(Error(Nil)) } +} - pub fn url_encode64_test() { - <<255, 127, 254, 252>> - |> base.url_encode64(True) - |> should.equal("_3_-_A==") +pub fn url_encode64_test() { + <<255, 127, 254, 252>> + |> base.url_encode64(True) + |> should.equal("_3_-_A==") - <<255, 127, 254, 252>> - |> base.url_encode64(False) - |> should.equal("_3_-_A") + <<255, 127, 254, 252>> + |> base.url_encode64(False) + |> should.equal("_3_-_A") - <<0, 0, 0>> - |> base.url_encode64(True) - |> should.equal("AAAA") + <<0, 0, 0>> + |> base.url_encode64(True) + |> should.equal("AAAA") - <<>> - |> base.url_encode64(True) - |> should.equal("") - } + <<>> + |> base.url_encode64(True) + |> should.equal("") +} +if erlang { pub fn url_decode64_test() { "_3_-_A==" |> base.url_decode64() |