diff --git a/CHANGELOG.md b/CHANGELOG.md index 11627a1d..224a4046 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## v0.39.0 - Unreleased +- Added `bit_array.split_once` to split a bit_array. - Fixed `list.window` entering an endless recursive loop for `n` = 0. - The `min` and `max` functions of the `order` module have been deprecated. - The `dict` and `set` modules gain the `is_empty` function. diff --git a/src/gleam/bit_array.gleam b/src/gleam/bit_array.gleam index c112eb38..e5db5bbe 100644 --- a/src/gleam/bit_array.gleam +++ b/src/gleam/bit_array.gleam @@ -206,3 +206,36 @@ fn do_inspect(input: BitArray, accumulator: String) -> String { _ -> accumulator } } + +/// Finds the position of a bit pattern within a bit array. +/// +/// ## Examples +/// +/// ```gleam +/// index_of(<<0, 1, 2, 3, 4, 5, 6, 7>>, <<3, 4, 5>>) +/// // -> "3" +/// ``` +/// +@external(erlang, "gleam_stdlib", "bit_array_index_of") +@external(javascript, "../gleam_stdlib.mjs", "bit_array_index_of") +pub fn index_of(haystack: BitArray, needle: BitArray) -> Int + +// error is returned if not found. +/// Splits a bit array into left and right parts at the bit pattern provided, an +/// +/// ## Examples +/// +/// ```gleam +/// split_once(<<0, 1, 2, 3, 4, 5, 6, 7>>, <<3, 4, 5>>) +/// // -> Ok(<<0, 1, 2>>, <<6, 7>>) +/// +/// split_once(<<0, 1, 2, 3, 4, 5, 6, 7>>, <<5, 4, 3>>) +/// // -> Error(Nil) +/// ``` +/// +@external(erlang, "gleam_stdlib", "bit_array_split_once") +@external(javascript, "../gleam_stdlib.mjs", "bit_array_split_once") +pub fn split_once( + haystack: BitArray, + needle: BitArray, +) -> Result(#(BitArray, BitArray), Nil) diff --git a/src/gleam_stdlib.erl b/src/gleam_stdlib.erl index b95d9f82..dd3dd990 100644 --- a/src/gleam_stdlib.erl +++ b/src/gleam_stdlib.erl @@ -13,7 +13,8 @@ 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, base16_decode/1, string_replace/3 + crop_string/2, base16_decode/1, string_replace/3, bit_array_index_of/2, + bit_array_split_once/2 ]). %% Taken from OTP's uri_string module @@ -536,3 +537,23 @@ base16_decode(String) -> string_replace(String, Pattern, Replacement) -> string:replace(String, Pattern, Replacement, all). + +bit_array_index_of(Haystack, Needle) -> + case binary:match(Haystack, Needle) of + {Pos, _Len} -> Pos; + _ -> -1 + end. + +bit_array_split_once(Haystack, Needle) -> + try + case Needle of + <<>> -> {ok, {<<>>, Haystack}}; + _ -> case binary:split(Haystack, Needle) of + [Part1, Part2] -> {ok, {Part1, Part2}}; + _ -> {error, nil} + end + end + catch + error:badarg -> {error, nil} + end. + diff --git a/src/gleam_stdlib.mjs b/src/gleam_stdlib.mjs index a837e1dc..507c0dec 100644 --- a/src/gleam_stdlib.mjs +++ b/src/gleam_stdlib.mjs @@ -425,7 +425,7 @@ export function compile_regex(pattern, options) { export function regex_split(regex, string) { return List.fromArray( string.split(regex).map((item) => (item === undefined ? "" : item)), - ); + ); } export function regex_scan(regex, string) { @@ -864,3 +864,38 @@ export function base16_decode(string) { export function bit_array_inspect(bits) { return `<<${[...bits.buffer].join(", ")}>>`; } + +export function bit_array_index_of(haystack, needle) { + if (needle.buffer.length > haystack.buffer.length) { + return -1; + } + + for (let i = 0; i <= haystack.buffer.length - needle.buffer.length; i++) { + let found = true; + for (let j = 0; j < needle.buffer.length; j++) { + if (haystack.buffer[i + j] !== needle.buffer[j]) { + found = false; + break; + } + } + if (found) { + return i; + } + } + + return -1; +} + +export function bit_array_split_once(haystack, needle) { + let index = bit_array_index_of(haystack, needle); + + if (index === -1) { + return new Error(Nil); + } + + const before = new BitArray(haystack.buffer.slice(0, index)); + const after = new BitArray(haystack.buffer.slice(index + needle.buffer.length)); + + return new Ok([before, after]); +} + diff --git a/test/gleam/bit_array_test.gleam b/test/gleam/bit_array_test.gleam index 7c4a5f4d..f191e399 100644 --- a/test/gleam/bit_array_test.gleam +++ b/test/gleam/bit_array_test.gleam @@ -308,3 +308,35 @@ pub fn inspect_partial_bytes_test() { bit_array.inspect(<<5:3, 11:4, 1:2>>) |> should.equal("<<182, 1:size(1)>>") } + +pub fn index_of_found_test() { + <<"Hello, World":utf8>> + |> bit_array.index_of(<<", ":utf8>>) + |> should.equal(5) +} + +pub fn index_of_not_found_test() { + <<"Hello, World":utf8>> + |> bit_array.index_of(<<"Joe":utf8>>) + |> should.equal(-1) +} + +pub fn split_once_found_test() { + <<"Hello, World":utf8>> + |> bit_array.split_once(<<", ":utf8>>) + |> should.be_ok + |> should.equal(#(<<"Hello":utf8>>, <<"World":utf8>>)) +} + +pub fn split_once_empty_needle_test() { + <<"Hello, World":utf8>> + |> bit_array.split_once(<<>>) + |> should.be_ok + |> should.equal(#(<<>>, <<"Hello, World":utf8>>)) +} + +pub fn split_once_not_found_test() { + <<"Hello, World":utf8>> + |> bit_array.split_once(<<"Joe":utf8>>) + |> should.be_error +}