Skip to content

Commit 48ee55c

Browse files
authored
[ffi] Utf8.fromUtf8(): allow passing the length (dart-archive/ffi#61)
The string could be non-zero-terminated, for example, or even if it is, the length may be already known, in which case an unnecessary strlen() call can be avoided.
1 parent e02da07 commit 48ee55c

File tree

2 files changed

+46
-7
lines changed

2 files changed

+46
-7
lines changed

pkgs/ffi/lib/src/utf8.dart

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,20 @@ class Utf8 extends Struct {
3131

3232
/// Creates a [String] containing the characters UTF-8 encoded in [string].
3333
///
34-
/// The [string] must be a zero-terminated byte sequence of valid UTF-8
35-
/// encodings of Unicode scalar values. A [FormatException] is thrown if the
36-
/// input is malformed. See [Utf8Decoder] for details on decoding.
34+
/// Either the [string] must be zero-terminated or its [length] — the
35+
/// number of bytes — must be specified as a non-negative value. The
36+
/// byte sequence must be valid UTF-8 encodings of Unicode scalar values. A
37+
/// [FormatException] is thrown if the input is malformed. See [Utf8Decoder]
38+
/// for details on decoding.
3739
///
3840
/// Returns a Dart string containing the decoded code points.
39-
static String fromUtf8(Pointer<Utf8> string) {
40-
final int length = strlen(string);
41-
return utf8.decode(Uint8List.view(
42-
string.cast<Uint8>().asTypedList(length).buffer, 0, length));
41+
static String fromUtf8(Pointer<Utf8> string, {int? length}) {
42+
if (length != null) {
43+
RangeError.checkNotNegative(length, 'length');
44+
} else {
45+
length = strlen(string);
46+
}
47+
return utf8.decode(string.cast<Uint8>().asTypedList(length));
4348
}
4449

4550
/// Convert a [String] to a UTF-8 encoded zero-terminated C string.

pkgs/ffi/test/utf8_test.dart

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,38 @@ void main() {
5555
final Pointer<Utf8> utf8 = _bytesFromList([0x80, 0x00]).cast();
5656
expect(() => Utf8.fromUtf8(utf8), throwsA(isFormatException));
5757
});
58+
59+
test('fromUtf8 ASCII with length', () {
60+
final Pointer<Utf8> utf8 = _bytesFromList(
61+
[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 10, 0]).cast();
62+
final String end = Utf8.fromUtf8(utf8, length: 5);
63+
expect(end, 'Hello');
64+
});
65+
66+
test('fromUtf8 emoji with length', () {
67+
final Pointer<Utf8> utf8 = _bytesFromList(
68+
[240, 159, 152, 142, 240, 159, 145, 191, 240, 159, 146, 172, 0]).cast();
69+
final String end = Utf8.fromUtf8(utf8, length: 4);
70+
expect(end, '😎');
71+
});
72+
73+
test('fromUtf8 with zero length', () {
74+
final Pointer<Utf8> utf8 = _bytesFromList(
75+
[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 10, 0]).cast();
76+
final String end = Utf8.fromUtf8(utf8, length: 0);
77+
expect(end, '');
78+
});
79+
80+
test('fromUtf8 with negative length', () {
81+
final Pointer<Utf8> utf8 = _bytesFromList(
82+
[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 10, 0]).cast();
83+
expect(() => Utf8.fromUtf8(utf8, length: -1), throwsRangeError);
84+
});
85+
86+
test('fromUtf8 with length and containing a zero byte', () {
87+
final Pointer<Utf8> utf8 = _bytesFromList(
88+
[72, 101, 108, 108, 111, 0, 87, 111, 114, 108, 100, 33, 10]).cast();
89+
final String end = Utf8.fromUtf8(utf8, length: 13);
90+
expect(end, 'Hello\x00World!\n');
91+
});
5892
}

0 commit comments

Comments
 (0)