Skip to content
File

Blob: src/workerd/api/encoding-test.c++

4.0 KB
1// Copyright (c) 2025 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5#include "encoding.h"
6 
7#include <kj/test.h>
8 
9namespace workerd::api {
10namespace test {
11 
12// These tests verify the findBestFit() function used by TextEncoder.encodeInto().
13//
14// bestFit(input, bufferSize) returns the number of input code units that can be
15// fully converted to UTF-8 and fit within the given output buffer size in bytes.
16//
17// Different characters expand to different UTF-8 byte lengths:
18// - ASCII (U+0000-U+007F): 1 byte per code unit
19// - Latin-1 extended (U+0080-U+00FF): 2 bytes per code unit
20// - BMP characters (U+0100-U+FFFF): 2-3 bytes per code unit
21// - Supplementary characters (U+10000+): 4 bytes, encoded as surrogate pairs in UTF-16
22//
23// The function must never split a surrogate pair, so if there's only room for part of
24// a multi-byte character, it stops before that character.
25KJ_TEST("BestFitASCII") {
26 // If there's zero input or output space, the answer is zero.
27 KJ_ASSERT(bestFit("", 0) == 0);
28 KJ_ASSERT(bestFit("a", 0) == 0);
29 KJ_ASSERT(bestFit("aa", 0) == 0);
30 KJ_ASSERT(bestFit("aaa", 0) == 0);
31 KJ_ASSERT(bestFit("aaaa", 0) == 0);
32 KJ_ASSERT(bestFit("aaaaa", 0) == 0);
33 KJ_ASSERT(bestFit("", 0) == 0);
34 KJ_ASSERT(bestFit("", 1) == 0);
35 KJ_ASSERT(bestFit("", 2) == 0);
36 KJ_ASSERT(bestFit("", 3) == 0);
37 KJ_ASSERT(bestFit("", 4) == 0);
38 KJ_ASSERT(bestFit("", 5) == 0);
39 // Zero cases with two-byte strings.
40 KJ_ASSERT(bestFit(u"", 0) == 0);
41 KJ_ASSERT(bestFit(u"€", 0) == 0);
42 KJ_ASSERT(bestFit(u"€€", 0) == 0);
43 KJ_ASSERT(bestFit(u"€€€", 0) == 0);
44 KJ_ASSERT(bestFit(u"€€€€", 0) == 0);
45 KJ_ASSERT(bestFit(u"€€€€€", 0) == 0);
46 KJ_ASSERT(bestFit(u"", 0) == 0);
47 KJ_ASSERT(bestFit(u"", 1) == 0);
48 KJ_ASSERT(bestFit(u"", 2) == 0);
49 KJ_ASSERT(bestFit(u"", 3) == 0);
50 KJ_ASSERT(bestFit(u"", 4) == 0);
51 KJ_ASSERT(bestFit(u"", 5) == 0);
52 // Small buffers that only just fit.
53 KJ_ASSERT(bestFit(u"a", 1) == 1);
54 KJ_ASSERT(bestFit(u"å", 2) == 1);
55 KJ_ASSERT(bestFit(u"€", 3) == 1);
56 KJ_ASSERT(bestFit(u"😹", 4) == 2);
57 // Small buffers that don't fit.
58 KJ_ASSERT(bestFit(u"å", 1) == 0);
59 KJ_ASSERT(bestFit(u"€", 2) == 0);
60 KJ_ASSERT(bestFit(u"😹", 3) == 0);
61 // Don't chop a surrogate pair.
62 KJ_ASSERT(bestFit(u"1😹", 4) == 1);
63 KJ_ASSERT(bestFit(u"12😹", 5) == 2);
64 KJ_ASSERT(bestFit(u"123😹", 6) == 3);
65 KJ_ASSERT(bestFit(u"1234😹", 7) == 4);
66 KJ_ASSERT(bestFit(u"12345😹", 8) == 5);
67 // Some bigger ones just for fun.
68 KJ_ASSERT(bestFit(u"😹😹😹😹😹😹", 0) == 0);
69 KJ_ASSERT(bestFit(u"😹😹😹😹😹😹", 1) == 0);
70 KJ_ASSERT(bestFit(u"😹😹😹😹😹😹", 2) == 0);
71 KJ_ASSERT(bestFit(u"😹😹😹😹😹😹", 3) == 0);
72 KJ_ASSERT(bestFit(u"😹😹😹😹😹😹", 4) == 2);
73 KJ_ASSERT(bestFit(u"😹😹😹😹😹😹", 5) == 2);
74 KJ_ASSERT(bestFit(u"😹😹😹😹😹😹", 6) == 2);
75 KJ_ASSERT(bestFit(u"😹😹😹😹😹😹", 7) == 2);
76 KJ_ASSERT(bestFit(u"😹😹😹😹😹😹", 8) == 4);
77 KJ_ASSERT(bestFit(u"😹😹😹😹😹😹", 9) == 4);
78 KJ_ASSERT(bestFit(u"0😹😹😹😹😹😹", 9) == 5); // 0😹😹 is 5 and takes 9.
79 KJ_ASSERT(bestFit(u"01😹😹😹😹😹😹", 9) == 4); // 01😹 is 4 and takes 6.
80 KJ_ASSERT(bestFit(u"012😹😹😹😹😹😹", 9) == 5); // 012😹 is 5 and takes 7.
81 KJ_ASSERT(bestFit(u"0123😹😹😹😹😹😹", 9) == 6); // 0123😹 is 6 and takes 8.
82 KJ_ASSERT(bestFit(u"01234😹😹😹😹😹😹", 9) == 7); // 01234😹 is 7 and takes 9.
83 KJ_ASSERT(bestFit(u"012345😹😹😹😹😹😹", 9) == 6); // 012345 is 6 and takes 6.
84 KJ_ASSERT(bestFit(u"0123456😹😹😹😹😹😹", 9) == 7); // 0123456 is 7 and takes 7.
85 KJ_ASSERT(bestFit(u"01234567😹😹😹😹😹😹", 9) == 8); // 0123456 is 8 and takes 8.
86 KJ_ASSERT(bestFit(u"012345678😹😹😹😹😹😹", 9) == 9); // 0123456 is 9 and takes 9.
87}
88 
89} // namespace test
90} // namespace workerd::api