aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2021-09-07 19:53:09 +0100
committerLouis Pilfold <louis@lpil.uk>2021-09-07 19:53:09 +0100
commitf869915d92bf90c385ddaaa6c059b25784364ab0 (patch)
treec348709049004310d94f5160bc16eb19a05a44de
parent43dad5dbfd3ef19257cbf8e781b0e8e0697b6b95 (diff)
downloadgleam_stdlib-f869915d92bf90c385ddaaa6c059b25784364ab0.tar.gz
gleam_stdlib-f869915d92bf90c385ddaaa6c059b25784364ab0.zip
base64 encode in JS
-rw-r--r--src/gleam/base.gleam47
-rw-r--r--src/gleam_stdlib.js44
-rw-r--r--test/gleam/base_test.gleam75
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()