Skip to content

Commit 7ffa22b

Browse files
authored
Add 'string::utf8ByteLength' (#1412)
`string::utf8ByteLength` is `char::utf8ByteCount` but for whole strings: efficient on NodeJS and LLVM, but I also included a version written in Effekt because I don't know what Chez does...
1 parent 628acac commit 7ffa22b

4 files changed

Lines changed: 51 additions & 0 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
: 0 (internal: 0, bytearray: 0)
2+
a: 1 (internal: 1, bytearray: 1)
3+
ab: 2 (internal: 2, bytearray: 2)
4+
abc: 3 (internal: 3, bytearray: 3)
5+
é: 2 (internal: 2, bytearray: 2)
6+
λ: 2 (internal: 2, bytearray: 2)
7+
💩: 4 (internal: 4, bytearray: 4)
8+
aé💩b: 8 (internal: 8, bytearray: 8)
9+
§é💩: 8 (internal: 8, bytearray: 8)
10+
©: 2 (internal: 2, bytearray: 2)
11+
¢: 2 (internal: 2, bytearray: 2)
12+
€: 3 (internal: 3, bytearray: 3)
13+
漢: 3 (internal: 3, bytearray: 3)
14+
अ: 3 (internal: 3, bytearray: 3)
15+
😀: 4 (internal: 4, bytearray: 4)
16+
𝄞: 4 (internal: 4, bytearray: 4)
17+
𐍈: 4 (internal: 4, bytearray: 4)
18+
مرحبا: 10 (internal: 10, bytearray: 10)
19+
👨‍👩‍👧‍👦: 25 (internal: 25, bytearray: 25)
20+
é: 3 (internal: 3, bytearray: 3)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
def one(str: String) = {
2+
val bytes = str.utf8ByteLength
3+
val bytes2 = str.internal::utf8ByteLength
4+
val bytes3 = str.bytearray::fromString.size
5+
println(s"${str}: ${bytes.show} (internal: ${bytes2.show}, bytearray: ${bytes3.show})")
6+
}
7+
8+
def main() = {
9+
val cases = ["", "a", "ab", "abc", "é", "λ", "💩", "aé💩b", "§é💩", "©", "¢", "€", "漢", "अ", "😀", "𝄞", "𐍈", "مرحبا", "👨‍👩‍👧‍👦", "e\u0301"]
10+
for { cases.each } { (s: String) => one(s) }
11+
}

libraries/common/effekt.effekt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ extern def infixPlusPlus(s1: String, s2: String) at {}: String =
152152
ret %Pos %spz
153153
"""
154154

155+
/// Beware that this function is backend-dependent.
156+
/// If you want to get the number of bytes that a string would take up in a UTF-8 encoding,
157+
/// use `string::utf8ByteLength` instead.
155158
extern def length(str: String) at {}: Int =
156159
js "${str}.length"
157160
chez "(string-length ${str})"

libraries/common/string.effekt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,23 @@ def lastIndexOf(str: String, sub: String, from: Int): Option[Int] = {
204204
go(from)
205205
}
206206

207+
namespace internal {
208+
def utf8ByteLength(str: String): Int = stream::sum {
209+
for {str.each} { (c: Char) =>
210+
do emit(c.utf8ByteCount)
211+
}
212+
}
213+
}
214+
215+
/// Returns the number of bytes that the given `str` would take up in a UTF-8 encoding.
216+
extern def utf8ByteLength(str: String) at {io, global}: Int =
217+
jsNode "Buffer.byteLength(${str}, 'utf8')"
218+
llvm """
219+
%x = call %Int @c_bytearray_size(%Pos ${str})
220+
ret %Int %x
221+
""" // via bytearray::fromString(str).length
222+
default { internal::utf8ByteLength(str) }
223+
207224
// TODO more efficient (native) implementations could be integrated as follows
208225

209226
// extern def indexOf(str: String, sub: String) at {}: Option[Int] =

0 commit comments

Comments
 (0)