From 4c3ea2086cf7662f79628f26abff15307ff5bce9 Mon Sep 17 00:00:00 2001 From: Richard Viney Date: Mon, 7 Oct 2024 10:57:50 +1300 Subject: [PATCH] Optimise string.pad_left, string.pad_right. Optimise string.slice on JS. --- src/gleam/string.gleam | 50 +++++++++++++++++++++--------------------- src/gleam_stdlib.mjs | 28 +++++++++++++++++++++++ 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/src/gleam/string.gleam b/src/gleam/string.gleam index 0906481c..1b2e3a05 100644 --- a/src/gleam/string.gleam +++ b/src/gleam/string.gleam @@ -1,7 +1,7 @@ //// Strings in Gleam are UTF-8 binaries. They can be written in your code as //// text surrounded by `"double quotes"`. -import gleam/iterator.{type Iterator} +import gleam/iterator import gleam/list import gleam/option.{type Option, None, Some} import gleam/order @@ -222,13 +222,8 @@ pub fn slice(from string: String, at_index idx: Int, length len: Int) -> String } @external(erlang, "gleam_stdlib", "slice") -fn do_slice(string: String, idx: Int, len: Int) -> String { - string - |> to_graphemes - |> list.drop(idx) - |> list.take(len) - |> concat -} +@external(javascript, "../gleam_stdlib.mjs", "string_slice") +fn do_slice(string: String, idx: Int, len: Int) -> String /// Drops contents of the first `String` that occur before the second `String`. /// If the `from` string does not contain the `before` string, `from` is returned unchanged. @@ -489,13 +484,18 @@ fn do_join(strings: List(String), separator: String) -> String { /// // -> "121" /// ``` /// -pub fn pad_left(string: String, to desired_length: Int, with pad_string: String) { +pub fn pad_left( + string: String, + to desired_length: Int, + with pad_string: String, +) -> String { let current_length = length(string) let to_pad_length = desired_length - current_length - padding(to_pad_length, pad_string) - |> iterator.append(iterator.single(string)) - |> iterator.to_list - |> concat + + case to_pad_length <= 0 { + True -> string + False -> padding(to_pad_length, pad_string) <> string + } } /// Pads a `String` on the right until it has a given length. @@ -521,22 +521,22 @@ pub fn pad_right( string: String, to desired_length: Int, with pad_string: String, -) { +) -> String { let current_length = length(string) let to_pad_length = desired_length - current_length - iterator.single(string) - |> iterator.append(padding(to_pad_length, pad_string)) - |> iterator.to_list - |> concat + + case to_pad_length <= 0 { + True -> string + False -> string <> padding(to_pad_length, pad_string) + } } -fn padding(size: Int, pad_string: String) -> Iterator(String) { - let pad_length = length(pad_string) - let num_pads = size / pad_length - let extra = size % pad_length - iterator.repeat(pad_string) - |> iterator.take(num_pads) - |> iterator.append(iterator.single(slice(pad_string, 0, extra))) +fn padding(size: Int, pad_string: String) -> String { + let pad_string_length = length(pad_string) + let num_pads = size / pad_string_length + let extra = size % pad_string_length + + repeat(pad_string, num_pads) <> slice(pad_string, 0, extra) } /// Removes whitespace on both sides of a `String`. diff --git a/src/gleam_stdlib.mjs b/src/gleam_stdlib.mjs index 1aa4753e..58a032e5 100644 --- a/src/gleam_stdlib.mjs +++ b/src/gleam_stdlib.mjs @@ -223,6 +223,34 @@ export function length(data) { return data.length; } +export function string_slice(string, idx, len) { + if (len <= 0 || idx >= string.length) { + return ""; + } + + const iterator = graphemes_iterator(string); + if (iterator) { + while (idx-- > 0) { + iterator.next(); + } + + let result = ""; + + while (len-- > 0) { + const v = iterator.next().value; + if (v === undefined) { + break; + } + + result += v.segment; + } + + return result; + } else { + return string.match(/./gsu).slice(idx, idx + len).join(""); + } +} + export function crop_string(string, substring) { return string.substring(string.indexOf(substring)); }