Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stdlib] Faster variadic arguments StringLiteral.join() when arguments are strings - WIP #3804

Draft
wants to merge 4 commits into
base: nightly
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions stdlib/src/builtin/string_literal.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ These are Mojo built-ins, so you don't need to import them.
from collections import List
from hashlib._hasher import _HashableWithHasher, _Hasher
from sys.ffi import c_char
from sys.intrinsics import _type_is_eq

from memory import UnsafePointer, memcpy

Expand Down Expand Up @@ -647,6 +648,38 @@ struct StringLiteral(
The joined string.
"""

# TODO(#3403): Simplify this when the linked conditional conformance
# feature is added. Runs a faster algorithm if the concrete types are
# able to be converted to a span of bytes.
@parameter
for i in range(len(VariadicList(Types))):
alias T = Types[i]

@parameter
if (
not _type_is_eq[T, String]()
and not _type_is_eq[T, StringLiteral]()
):
# TODO: This is not working as unpacked arguments are not supported by compiler.
return self.slow_join(*elems)
# TODO: This is not working as unpacked arguments are not supported by compiler.
return self.fast_join(*elems)

fn slow_join[*Types: Stringable](self, *elems: *Types) -> String:
"""Joins string elements using the current string as a delimiter.
This is a slower version of join as we don't know the total capacity of the
resulting string so we have to grow it on-the-fly and many reallocations
might happen.

Parameters:
Types: The types of the elements.

Args:
elems: The input values.

Returns:
The joined string.
"""
var result: String = ""
var is_first = True

Expand All @@ -661,6 +694,45 @@ struct StringLiteral(
elems.each[add_elt]()
return result

fn fast_join[
*Types: BytesCollectionElement
](self, *elems: *Types) -> String:
"""Joins string elements using the current string as a delimiter.
This is a faster version of join that works with types that can be converted
to a span of bytes so we can calculate the capacity of the resulting string.

Parameters:
Types: The types of the elements.

Args:
elems: The input values.

Returns:
The joined string.
"""

var is_first: Bool = True
var capacity: Int = 0

@parameter
fn add_capacity[T: BytesCollectionElement](a: T):
capacity += len(a.as_bytes())

elems.each[add_capacity]()

var result = String(capacity=capacity)

@parameter
fn add_elt[T: BytesCollectionElement](a: T):
if is_first:
is_first = False
else:
result += self
result += String(a.as_bytes())

elems.each[add_elt]()
return result

fn split(self, sep: String, maxsplit: Int = -1) raises -> List[String]:
"""Split the string literal by a separator.

Expand Down
Loading