HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
unicode_line_break.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
8#pragma once
9
10#include "unicode_break_opportunity.hpp"
11#include "ucd_general_categories.hpp"
12#include "ucd_grapheme_cluster_breaks.hpp"
13#include "ucd_line_break_classes.hpp"
14#include "ucd_east_asian_widths.hpp"
15#include "../utility/utility.hpp"
16#include "../macros.hpp"
17#include <cstdint>
18#include <vector>
19#include <algorithm>
20#include <numeric>
21
22
23
24namespace hi::inline v1 {
25namespace detail {
26
30 unicode_line_break_class original_class = unicode_line_break_class::XX;
31 unicode_line_break_class current_class = unicode_line_break_class::XX;
32 bool is_extended_pictographic = false;
33 bool is_Cn = false;
34 unicode_east_asian_width east_asian_width = unicode_east_asian_width::A;
35
36 constexpr unicode_line_break_info() noexcept = default;
37 constexpr unicode_line_break_info(unicode_line_break_info const&) noexcept = default;
38 constexpr unicode_line_break_info(unicode_line_break_info&&) noexcept = default;
39 constexpr unicode_line_break_info& operator=(unicode_line_break_info const&) noexcept = default;
40 constexpr unicode_line_break_info& operator=(unicode_line_break_info&&) noexcept = default;
41
42 constexpr explicit unicode_line_break_info(
43 unicode_line_break_class break_class,
44 bool is_Cn,
45 bool is_extended_pictographic,
46 unicode_east_asian_width east_asian_width) noexcept :
47 original_class(break_class),
48 current_class(break_class),
49 is_Cn(is_Cn),
50 is_extended_pictographic(is_extended_pictographic),
51 east_asian_width(east_asian_width)
52 {
53 }
54
55 constexpr explicit operator unicode_line_break_class() const noexcept
56 {
57 return current_class;
58 }
59
60 constexpr unicode_line_break_info& operator|=(unicode_line_break_class rhs) noexcept
61 {
62 current_class = rhs;
63 return *this;
64 }
65
66 [[nodiscard]] constexpr bool operator==(unicode_line_break_class rhs) const noexcept
67 {
68 return current_class == rhs;
69 }
70
71 [[nodiscard]] constexpr bool operator==(unicode_east_asian_width rhs) const noexcept
72 {
73 return east_asian_width == rhs;
74 }
75};
76
78using unicode_line_break_info_iterator = unicode_line_break_info_vector::iterator;
79using unicode_line_break_info_const_iterator = unicode_line_break_info_vector::const_iterator;
80
81template<typename It, typename ItEnd, typename CodePointFunc>
82[[nodiscard]] constexpr std::vector<unicode_line_break_info>
83unicode_LB1(It first, ItEnd last, CodePointFunc const& code_point_func) noexcept
84{
86 r.reserve(std::distance(first, last));
87
88 for (auto it = first; it != last; ++it) {
89 hilet code_point = code_point_func(*it);
90 hilet east_asian_width = ucd_get_east_asian_width(code_point);
91 hilet break_class = ucd_get_line_break_class(code_point);
92 hilet general_category = ucd_get_general_category(code_point);
93 hilet grapheme_cluster_break = ucd_get_grapheme_cluster_break(code_point);
94
95 hilet resolved_break_class = [&]() {
96 switch (break_class) {
97 using enum unicode_line_break_class;
98 case AI:
99 case SG:
100 case XX:
101 return AL;
102 case CJ:
103 return NS;
104 case SA:
105 return is_Mn_or_Mc(general_category) ? CM : AL;
106 default:
107 return break_class;
108 }
109 }();
110
111 r.emplace_back(
112 resolved_break_class,
113 general_category == unicode_general_category::Cn,
114 grapheme_cluster_break == unicode_grapheme_cluster_break::Extended_Pictographic,
115 east_asian_width);
116 }
117
118 return r;
119}
120
121[[nodiscard]] constexpr void unicode_LB2_3(unicode_break_vector& opportunities) noexcept
122{
123 hi_axiom(not opportunities.empty());
124 // LB2
125 opportunities.front() = unicode_break_opportunity::no;
126 // LB3
127 opportunities.back() = unicode_break_opportunity::mandatory;
128}
129
130template<typename MatchFunc>
131constexpr void unicode_LB_walk(
132 unicode_break_vector& opportunities,
134 MatchFunc match_func) noexcept
135{
136 using enum unicode_line_break_class;
137
138 if (infos.empty()) {
139 return;
140 }
141
142 auto cur = infos.begin();
143 hilet last = infos.end() - 1;
144 hilet last2 = infos.end();
145 auto opportunity = opportunities.begin() + 1;
146
147 auto cur_sp_class = XX;
148 auto cur_nu_class = XX;
149 auto prev_class = XX;
150 auto num_ri = 0_uz;
151 while (cur != last) {
152 hilet next = cur + 1;
153 hilet cur_class = unicode_line_break_class{*cur};
154 hilet next2_class = cur + 2 == last2 ? XX : unicode_line_break_class{*(cur + 2)};
155
156 // Keep track of classes followed by zero or more SP.
157 if (cur_class != SP) {
158 cur_sp_class = cur_class;
159 }
160
161 // Keep track of a "NU (NU|SY|IS)*" and "NU (NU|SY|IS)* (CL|CP)?".
162 if (cur_nu_class == CL) {
163 // Only a single CL|CP class may be at the end, then the number is closed.
164 cur_nu_class = XX;
165 } else if (cur_nu_class == NU) {
166 if (cur_class == CL or cur_class == CP) {
167 cur_nu_class = CL;
168 } else if (cur_class != NU and cur_class != SY and cur_class != IS) {
169 cur_nu_class = XX;
170 }
171 } else if (cur_class == NU) {
172 cur_nu_class = NU;
173 }
174
175 // Keep track of consecutive RI, but only count the actual RIs.
176 if (cur->original_class == RI) {
177 ++num_ri;
178 } else if (*cur != RI) {
179 num_ri = 0;
180 }
181
182 if (*opportunity == unicode_break_opportunity::unassigned) {
183 *opportunity = match_func(prev_class, cur, next, next2_class, cur_sp_class, cur_nu_class, num_ri);
184 }
185
186 prev_class = cur_class;
187 cur = next;
188 ++opportunity;
189 }
190}
191
192constexpr void unicode_LB4_8a(unicode_break_vector& opportunities, std::vector<unicode_line_break_info> const& infos) noexcept
193{
194 unicode_LB_walk(
195 opportunities, infos, [](hilet prev, hilet cur, hilet next, hilet next2, hilet cur_sp, hilet cur_nu, hilet num_ri) {
196 using enum unicode_break_opportunity;
197 using enum unicode_line_break_class;
198 if (*cur == BK) {
199 return mandatory; // LB4: 4.0
200 } else if (*cur == CR and *next == LF) {
201 return no; // LB5: 5.01
202 } else if (*cur == CR or *cur == LF or *cur == NL) {
203 return mandatory; // LB5: 5.02, 5.03, 5.04
204 } else if (*next == BK or *next == CR or *next == LF or *next == NL) {
205 return no; // LB6: 6.0
206 } else if (*next == SP or *next == ZW) {
207 return no; // LB7: 7.01, 7.02
208 } else if (cur_sp == ZW) {
209 return yes; // LB8: 8.0
210 } else if (*cur == ZWJ) {
211 return no; // LB8a: 8.1
212 } else {
213 return unassigned;
214 }
215 });
216}
217
218constexpr void unicode_LB9(unicode_break_vector& opportunities, std::vector<unicode_line_break_info>& infos) noexcept
219{
220 using enum unicode_line_break_class;
221 using enum unicode_break_opportunity;
222
223 if (infos.empty()) {
224 return;
225 }
226
227 auto cur = infos.begin();
228 hilet last = infos.end() - 1;
229 auto opportunity = opportunities.begin() + 1;
230
231 auto X = XX;
232 while (cur != last) {
233 hilet next = cur + 1;
234
235 if ((*cur == CM or *cur == ZWJ) and X != XX) {
236 // Treat all CM/ZWJ as X (if there is an X).
237 *cur |= X;
238 } else {
239 // Reset X on non-CM/ZWJ.
240 X = XX;
241 }
242
243 if ((*cur != BK and *cur != CR and *cur != LF and *cur != NL and *cur != SP and *cur != ZW) and
244 (*next == CM or *next == ZWJ)) {
245 // [^BK CR LF NL SP ZW] x [CM ZWJ]*
246 *opportunity = no;
247
248 if (X == XX) {
249 // The first character of [^BK CR LF NL SP ZW] x [CM ZWJ]* => X
250 X = static_cast<unicode_line_break_class>(*cur);
251 }
252 }
253
254 cur = next;
255 ++opportunity;
256 }
257}
258
259constexpr void unicode_LB10(std::vector<unicode_line_break_info>& infos) noexcept
260{
261 using enum unicode_line_break_class;
262
263 for (auto& x : infos) {
264 if (x == CM or x == ZWJ) {
265 x |= AL;
266 }
267 }
268}
269
270constexpr void unicode_LB11_31(unicode_break_vector& opportunities, std::vector<unicode_line_break_info> const& infos) noexcept
271{
272 unicode_LB_walk(
273 opportunities, infos, [&](hilet prev, hilet cur, hilet next, hilet next2, hilet cur_sp, hilet cur_nu, hilet num_ri) {
274 using enum unicode_break_opportunity;
275 using enum unicode_line_break_class;
276 using enum unicode_east_asian_width;
277
278 if (*cur == WJ or *next == WJ) {
279 return no; // LB11: 11.01, 11.02
280 } else if (*cur == GL) {
281 return no; // LB12: 12.0
282 } else if (*cur != SP and *cur != BA and *cur != HY and *next == GL) {
283 return no; // LB12a: 12.1
284 } else if (*next == CL or *next == CP or *next == EX or *next == IS or *next == SY) {
285 return no; // LB13: 13.0
286 } else if (cur_sp == OP) {
287 return no; // LB14: 14.0
288 } else if (cur_sp == QU and *next == OP) {
289 return no; // LB15: 15.0
290 } else if ((cur_sp == CL or cur_sp == CP) and *next == NS) {
291 return no; // LB16: 16.0
292 } else if (cur_sp == B2 and *next == B2) {
293 return no; // LB17: 17.0
294 } else if (*cur == SP) {
295 return yes; // LB18: 18.0
296 } else if (*cur == QU or *next == QU) {
297 return no; // LB19: 19.01, 19.02
298 } else if (*cur == CB or *next == CB) {
299 return yes; // LB20: 20.01, 20.02
300 } else if (*cur == BB or *next == BA or *next == HY or *next == NS) {
301 return no; // LB21: 21.01, 21.02, 21.03, 21.04
302 } else if (prev == HL and (*cur == HY or *cur == BA)) {
303 return no; // LB21a: 21.1
304 } else if (*cur == SY and *next == HL) {
305 return no; // LB21b: 21.2
306 } else if (*next == IN) {
307 return no; // LB22: 22.0
308 } else if ((*cur == AL or *cur == HL) and *next == NU) {
309 return no; // LB23: 23.02
310 } else if (*cur == NU and (*next == AL or *next == HL)) {
311 return no; // LB23: 23.03
312 } else if (*cur == PR and (*next == ID or *next == EB or *next == EM)) {
313 return no; // LB23a: 23.12
314 } else if ((*cur == ID or *cur == EB or *cur == EM) and *next == PO) {
315 return no; // LB23a: 23.13
316 } else if ((*cur == PR or *cur == PO) and (*next == AL or *next == HL)) {
317 return no; // LB24: 24.02
318 } else if ((*cur == AL or *cur == HL) and (*next == PR or *next == PO)) {
319 return no; // LB24: 24.03
320 } else if (
321 (*cur == PR or *cur == PO) and ((*next == OP and next2 == NU) or (*next == HY and next2 == NU) or *next == NU)) {
322 return no; // LB25: 25.01
323 } else if ((*cur == OP or *cur == HY) and *next == NU) {
324 return no; // LB25: 25.02
325 } else if (*cur == NU and (*next == NU or *next == SY or *next == IS)) {
326 return no; // LB25: 25.03
327 } else if (cur_nu == NU and (*next == NU or *next == SY or *next == IS or *next == CL or *next == CP)) {
328 return no; // LB25: 25.04
329 } else if ((cur_nu == NU or cur_nu == CL) and (*next == PO or *next == PR)) {
330 return no; // LB25: 25.05
331 } else if (*cur == JL and (*next == JL or *next == JV or *next == H2 or *next == H3)) {
332 return no; // LB26: 26.01
333 } else if ((*cur == JV or *cur == H2) and (*next == JV or *next == JT)) {
334 return no; // LB26: 26.02
335 } else if ((*cur == JT or *cur == H3) and *next == JT) {
336 return no; // LB26: 26.03
337 } else if ((*cur == JL or *cur == JV or *cur == JT or *cur == H2 or *cur == H3) and *next == PO) {
338 return no; // LB27: 27.01
339 } else if (*cur == PR and (*next == JL or *next == JV or *next == JT or *next == H2 or *next == H3)) {
340 return no; // LB27: 27.02
341 } else if ((*cur == AL or *cur == HL) and (*next == AL or *next == HL)) {
342 return no; // LB28: 28.0
343 } else if (*cur == IS and (*next == AL or *next == HL)) {
344 return no; // LB29: 29.0
345 } else if ((*cur == AL or *cur == HL or *cur == NU) and (*next == OP and *next != F and *next != W and *next != H)) {
346 return no; // LB30: 30.01
347 } else if ((*cur == CP and *cur != F and *cur != W and *cur != H) and (*next == AL or *next == HL or *next == NU)) {
348 return no; // LB30: 30.02
349 } else if (*cur == RI and *next == RI and (num_ri % 2) == 1) {
350 return no; // LB30a: 30.11, 30.12, 30.13
351 } else if (*cur == EB and *next == EM) {
352 return no; // LB30b: 30.21
353 } else if (cur->is_extended_pictographic and cur->is_Cn and *next == EM) {
354 return no; // LB30b: 30.22
355 } else {
356 return yes; // LB31: 999.0
357 }
358 });
359}
360
367[[nodiscard]] constexpr float
368unicode_LB_width(std::vector<float>::const_iterator first, std::vector<float>::const_iterator last) noexcept
369{
370 if (first == last) {
371 return 0.0f;
372 }
373
374 auto rfirst = std::make_reverse_iterator(last);
375 auto rlast = std::make_reverse_iterator(first);
376
377 auto it = std::find_if(rfirst, rlast, [](hilet& width) {
378 return width >= 0.0;
379 });
380 return std::accumulate(it, rlast, 0.0f, [](float acc, hilet& width) {
381 return acc + abs(width);
382 });
383}
384
391[[nodiscard]] constexpr float unicode_LB_width(std::vector<float> const& widths, std::vector<size_t> const& lengths)
392{
393 auto max_width = 0.0f;
394 auto it = widths.begin();
395 for (auto length : lengths) {
396 inplace_max(max_width, unicode_LB_width(it, it + length));
397 it += length;
398 }
399 return max_width;
400}
401
409[[nodiscard]] constexpr bool
410unicode_LB_width_check(std::vector<float> const& widths, std::vector<size_t> const& lengths, float maximum_line_width) noexcept
411{
412 auto it = widths.begin();
413 for (auto length : lengths) {
414 if (unicode_LB_width(it, it + length) > maximum_line_width) {
415 return false;
416 }
417 it += length;
418 }
419 return true;
420}
421
426[[nodiscard]] constexpr std::vector<size_t> unicode_LB_mandatory_lines(unicode_break_vector const& opportunities) noexcept
427{
428 auto r = std::vector<size_t>{};
429
430 auto length = 0_uz;
431 for (auto it = opportunities.begin() + 1; it != opportunities.end(); ++it) {
432 ++length;
433 if (*it == unicode_break_opportunity::mandatory) {
434 r.push_back(length);
435 length = 0_uz;
436 }
437 }
438
439 return r;
440}
441
446[[nodiscard]] constexpr std::vector<size_t> unicode_LB_optional_lines(unicode_break_vector const& opportunities) noexcept
447{
448 auto r = std::vector<size_t>{};
449
450 auto length = 0_uz;
451 for (auto it = opportunities.begin() + 1; it != opportunities.end(); ++it) {
452 ++length;
453 if (*it != unicode_break_opportunity::no) {
454 r.push_back(length);
455 length = 0_uz;
456 }
457 }
458
459 return r;
460}
461
462[[nodiscard]] constexpr unicode_break_const_iterator unicode_LB_fast_fit_line(
463 unicode_break_const_iterator opportunity_it,
464 std::vector<float>::const_iterator width_it,
465 float maximum_line_width) noexcept
466{
467 using enum unicode_break_opportunity;
468
469 auto width = 0.0f;
470 auto end_of_line = opportunity_it;
471 while (true) {
472 width += abs(*width_it);
473 if (width > maximum_line_width) {
474 // This character makes the width too long.
475 return end_of_line;
476
477 } else if (*opportunity_it == mandatory) {
478 // This character is an end-of-line.
479 return opportunity_it;
480
481 } else if (*opportunity_it == yes) {
482 // This character is a valid break opportunity.
483 end_of_line = opportunity_it;
484 }
485
486 ++opportunity_it;
487 ++width_it;
488 }
489 std::unreachable();
490}
491
492[[nodiscard]] constexpr unicode_break_const_iterator unicode_LB_slow_fit_line(
493 unicode_break_const_iterator first,
494 unicode_break_const_iterator end_of_line,
495 std::vector<float>::const_iterator first_width,
496 float maximum_line_width) noexcept
497{
498 using enum unicode_break_opportunity;
499
500 // Carefully look forward for a break opportunity.
501 auto it = end_of_line;
502 while (true) {
503 hilet num_characters = std::distance(first, it + 1);
504 hilet line_width = unicode_LB_width(first_width, first_width + num_characters);
505
506 if (line_width <= maximum_line_width) {
507 if (*it == mandatory) {
508 // The next mandatory break fits in the maximum width.
509 return it;
510
511 } else if (*it == yes) {
512 // The next break opportunity fits in the maximum width.
513 end_of_line = it;
514 }
515 } else {
516 // This break opportunity doesn't fit within the maximum width. Use the previous break opportunity.
517 return end_of_line;
518 }
519
520 ++it;
521 }
522 std::unreachable();
523}
524
525[[nodiscard]] constexpr unicode_break_const_iterator
526unicode_LB_finish_fit_line(unicode_break_const_iterator first, unicode_break_const_iterator end_of_line) noexcept
527{
528 if (first == end_of_line) {
529 // We couldn't break the line to fit the maximum line width.
530 while (*end_of_line == unicode_break_opportunity::no) {
531 ++end_of_line;
532 }
533 }
534
535 // Return iterator past the end-of-line.
536 return end_of_line + 1;
537}
538
544 unicode_break_vector const& opportunities,
545 std::vector<float> const& widths,
546 float maximum_line_width) noexcept
547{
548 using enum unicode_break_opportunity;
549
550 auto r = std::vector<size_t>{};
551 if (widths.empty()) {
552 return r;
553 }
554
555 auto opportunity_it = opportunities.begin() + 1;
556 auto width_it = widths.begin();
557 while (width_it != widths.end()) {
558 // First quickly find when the line is too long.
559 auto opportunity_eol = unicode_LB_fast_fit_line(opportunity_it, width_it, maximum_line_width);
560 opportunity_eol = unicode_LB_slow_fit_line(opportunity_it, opportunity_eol, width_it, maximum_line_width);
561 opportunity_eol = unicode_LB_finish_fit_line(opportunity_it, opportunity_eol);
562
563 hilet num_characters = std::distance(opportunity_it, opportunity_eol);
564 r.push_back(num_characters);
565 opportunity_it += num_characters;
566 width_it += num_characters;
567 }
568
569 return r;
570}
571
580[[nodiscard]] constexpr std::pair<float, std::vector<size_t>>
581unicode_LB_maximum_width(unicode_break_vector const& opportunities, std::vector<float> const& char_widths)
582{
583 auto line_lengths = detail::unicode_LB_mandatory_lines(opportunities);
584 hilet width = detail::unicode_LB_width(char_widths, line_lengths);
585 return {width, std::move(line_lengths)};
586}
587
596[[nodiscard]] constexpr std::pair<float, std::vector<size_t>>
597unicode_LB_minimum_width(unicode_break_vector const& opportunities, std::vector<float> const& char_widths)
598{
599 auto line_lengths = detail::unicode_LB_optional_lines(opportunities);
600 hilet width = detail::unicode_LB_width(char_widths, line_lengths);
601 return {width, std::move(line_lengths)};
602}
603
613[[nodiscard]] constexpr std::pair<float, std::vector<size_t>>
614unicode_LB_width(unicode_break_vector const& opportunities, std::vector<float> const& char_widths, float maximum_line_width)
615{
616 auto line_lengths = detail::unicode_LB_fit_lines(opportunities, char_widths, maximum_line_width);
617 hilet width = detail::unicode_LB_width(char_widths, line_lengths);
618 return {width, std::move(line_lengths)};
619}
620
621} // namespace detail
622
630template<typename It, typename ItEnd, typename CodePointFunc>
631[[nodiscard]] inline unicode_break_vector
632unicode_line_break(It first, ItEnd last, CodePointFunc const& code_point_func) noexcept
633{
634 auto size = narrow_cast<size_t>(std::distance(first, last));
635 auto r = unicode_break_vector{size + 1, unicode_break_opportunity::unassigned};
636
637 auto infos = detail::unicode_LB1(first, last, code_point_func);
638 detail::unicode_LB2_3(r);
639 detail::unicode_LB4_8a(r, infos);
640 detail::unicode_LB9(r, infos);
641 detail::unicode_LB10(infos);
642 detail::unicode_LB11_31(r, infos);
643 return r;
644}
645
653[[nodiscard]] constexpr std::vector<size_t>
655{
656 // See if the lines after mandatory breaks will fit the width and return.
657 auto r = detail::unicode_LB_mandatory_lines(opportunities);
658 if (detail::unicode_LB_width_check(widths, r, maximum_line_width)) {
659 return r;
660 }
661
662 r = detail::unicode_LB_fit_lines(opportunities, widths, maximum_line_width);
663 hi_axiom(detail::unicode_LB_width_check(widths, r, maximum_line_width));
664 return r;
665}
666
667
668} // namespace hi::inline v1
constexpr float unicode_LB_width(std::vector< float >::const_iterator first, std::vector< float >::const_iterator last) noexcept
Calculate the width of a line.
Definition unicode_line_break.hpp:368
constexpr std::vector< size_t > unicode_LB_mandatory_lines(unicode_break_vector const &opportunities) noexcept
Get the length of each line when broken with mandatory breaks.
Definition unicode_line_break.hpp:426
constexpr std::vector< size_t > unicode_LB_optional_lines(unicode_break_vector const &opportunities) noexcept
Get the length of each line when broken with mandatory and optional breaks.
Definition unicode_line_break.hpp:446
constexpr std::pair< float, std::vector< size_t > > unicode_LB_minimum_width(unicode_break_vector const &opportunities, std::vector< float > const &char_widths)
Get the minimum width of the text.
Definition unicode_line_break.hpp:597
constexpr std::pair< float, std::vector< size_t > > unicode_LB_maximum_width(unicode_break_vector const &opportunities, std::vector< float > const &char_widths)
Get the maximum width of the text.
Definition unicode_line_break.hpp:581
constexpr std::vector< size_t > unicode_LB_fit_lines(unicode_break_vector const &opportunities, std::vector< float > const &widths, float maximum_line_width) noexcept
Get the length of each line when broken after folding text to a maximum width.
Definition unicode_line_break.hpp:543
constexpr bool unicode_LB_width_check(std::vector< float > const &widths, std::vector< size_t > const &lengths, float maximum_line_width) noexcept
Check if all the lines in the text fit the maximum width.
Definition unicode_line_break.hpp:410
DOXYGEN BUG.
Definition algorithm.hpp:16
unicode_break_vector unicode_line_break(It first, ItEnd last, CodePointFunc const &code_point_func) noexcept
The unicode line break algorithm UAX #14.
Definition unicode_line_break.hpp:632
unicode_line_break_class
Unicode line break class.
Definition ucd_line_break_classes.hpp:1012
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
Combined unicode_line_break_class and unicode_line_break_opportunity.
Definition unicode_line_break.hpp:29
T accumulate(T... args)
T begin(T... args)
T distance(T... args)
T find_if(T... args)
T move(T... args)
T next(T... args)
T push_back(T... args)
T reserve(T... args)