aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2023-10-26 13:03:22 +0100
committerLouis Pilfold <louis@lpil.uk>2023-10-26 13:07:39 +0100
commite0cefe61b7519d8356885b941c34bbf4763cb71c (patch)
treec6dbff1309805ecb6f1b0d9df6cb091552ec6970
parentd55bfcbfc6c6c836013e4b9b45eb70c729d5ca7b (diff)
downloadgleam_stdlib-e0cefe61b7519d8356885b941c34bbf4763cb71c.tar.gz
gleam_stdlib-e0cefe61b7519d8356885b941c34bbf4763cb71c.zip
Hex!
-rw-r--r--src/gleam/bit_array.gleam8
-rw-r--r--src/gleam_stdlib.erl9
-rw-r--r--src/gleam_stdlib.mjs19
-rw-r--r--test/gleam/bit_array_test.gleam60
4 files changed, 95 insertions, 1 deletions
diff --git a/src/gleam/bit_array.gleam b/src/gleam/bit_array.gleam
index b85782d..79860e9 100644
--- a/src/gleam/bit_array.gleam
+++ b/src/gleam/bit_array.gleam
@@ -147,3 +147,11 @@ pub fn base64_url_decode(encoded: String) -> Result(BitArray, Nil) {
|> string.replace("_", "/")
|> base64_decode()
}
+
+@external(erlang, "binary", "encode_hex")
+@external(javascript, "../gleam_stdlib.mjs", "base16_encode")
+pub fn base16_encode(input: BitArray) -> String
+
+@external(erlang, "gleam_stdlib", "base16_decode")
+@external(javascript, "../gleam_stdlib.mjs", "base16_decode")
+pub fn base16_decode(input: String) -> Result(BitArray, Nil)
diff --git a/src/gleam_stdlib.erl b/src/gleam_stdlib.erl
index 0ac4747..0e2136f 100644
--- a/src/gleam_stdlib.erl
+++ b/src/gleam_stdlib.erl
@@ -13,7 +13,7 @@
decode_tuple5/1, decode_tuple6/1, tuple_get/2, classify_dynamic/1, print/1,
println/1, print_error/1, println_error/1, inspect/1, float_to_string/1,
int_from_base_string/2, utf_codepoint_list_to_string/1, contains_string/2,
- crop_string/2
+ crop_string/2, base16_decode/1
]).
%% Taken from OTP's uri_string module
@@ -490,3 +490,10 @@ crop_string(String, Prefix) ->
contains_string(String, Substring) ->
is_bitstring(string:find(String, Substring)).
+
+base16_decode(String) ->
+ try
+ {ok, binary:decode_hex(String)}
+ catch
+ _:_ -> {error, nil}
+ end.
diff --git a/src/gleam_stdlib.mjs b/src/gleam_stdlib.mjs
index 645e22a..2a1a6cf 100644
--- a/src/gleam_stdlib.mjs
+++ b/src/gleam_stdlib.mjs
@@ -842,3 +842,22 @@ export function inspectBitArray(bits) {
export function inspectUtfCodepoint(codepoint) {
return `//utfcodepoint(${String.fromCodePoint(codepoint.value)})`;
}
+
+export function base16_encode(bit_array) {
+ let result = "";
+ for (const byte of bit_array.buffer) {
+ result += byte.toString(16).padStart(2, "0").toUpperCase();
+ }
+ return result;
+}
+
+export function base16_decode(string) {
+ const bytes = new Uint8Array(string.length / 2);
+ for (let i = 0; i < string.length; i += 2) {
+ const a = parseInt(string[i], 16);
+ const b = parseInt(string[i + 1], 16);
+ if (isNaN(a) || isNaN(b)) return new Error(Nil);
+ bytes[i / 2] = a * 16 + b;
+ }
+ return new Ok(new BitArray(bytes));
+}
diff --git a/test/gleam/bit_array_test.gleam b/test/gleam/bit_array_test.gleam
index 7095205..903b0c8 100644
--- a/test/gleam/bit_array_test.gleam
+++ b/test/gleam/bit_array_test.gleam
@@ -219,3 +219,63 @@ pub fn decode64_crash_regression_1_test() {
|> bit_array.base64_decode()
|> should.equal(Error(Nil))
}
+
+pub fn base16_test() {
+ bit_array.base16_encode(<<"":utf8>>)
+ |> should.equal("")
+
+ bit_array.base16_encode(<<"f":utf8>>)
+ |> should.equal("66")
+
+ bit_array.base16_encode(<<"fo":utf8>>)
+ |> should.equal("666F")
+
+ bit_array.base16_encode(<<"foo":utf8>>)
+ |> should.equal("666F6F")
+
+ bit_array.base16_encode(<<"foob":utf8>>)
+ |> should.equal("666F6F62")
+
+ bit_array.base16_encode(<<"fooba":utf8>>)
+ |> should.equal("666F6F6261")
+
+ bit_array.base16_encode(<<"foobar":utf8>>)
+ |> should.equal("666F6F626172")
+
+ bit_array.base16_encode(<<161, 178, 195, 212, 229, 246, 120, 145>>)
+ |> should.equal("A1B2C3D4E5F67891")
+}
+
+pub fn base16_decode_test() {
+ bit_array.base16_decode("")
+ |> should.equal(Ok(<<>>))
+
+ bit_array.base16_decode("66")
+ |> should.equal(Ok(<<"f":utf8>>))
+
+ bit_array.base16_decode("666F")
+ |> should.equal(Ok(<<"fo":utf8>>))
+
+ bit_array.base16_decode("666F6F")
+ |> should.equal(Ok(<<"foo":utf8>>))
+
+ bit_array.base16_decode("666F6F62")
+ |> should.equal(Ok(<<"foob":utf8>>))
+
+ bit_array.base16_decode("666F6F6261")
+ |> should.equal(Ok(<<"fooba":utf8>>))
+
+ bit_array.base16_decode("666F6F626172")
+ |> should.equal(Ok(<<"foobar":utf8>>))
+
+ bit_array.base16_decode("A1B2C3D4E5F67891")
+ |> should.equal(Ok(<<161, 178, 195, 212, 229, 246, 120, 145>>))
+
+ // Not a hex string
+ bit_array.base16_decode("?")
+ |> should.equal(Error(Nil))
+
+ // Lowercase hex
+ bit_array.base16_decode("a1b2c3d4e5f67891")
+ |> should.equal(Ok(<<161, 178, 195, 212, 229, 246, 120, 145>>))
+}