Skip to main content

boa_string/
builder.rs

1use crate::r#type::{InternalStringType, Latin1, Utf16};
2use crate::{JsStr, JsStrVariant, JsString, SequenceString, alloc_overflow};
3use std::{
4    alloc::{Layout, alloc, dealloc, realloc},
5    marker::PhantomData,
6    ops::{Add, AddAssign},
7    ptr::{self, NonNull},
8    str::{self},
9};
10
11/// A mutable builder to create instances of `JsString`.
12#[derive(Debug)]
13#[allow(private_bounds)]
14pub struct JsStringBuilder<D: InternalStringType> {
15    cap: usize,
16    len: usize,
17    inner: NonNull<SequenceString<D>>,
18    phantom_data: PhantomData<D>,
19}
20
21impl<D: InternalStringType> Default for JsStringBuilder<D> {
22    fn default() -> Self {
23        Self::new()
24    }
25}
26
27#[allow(private_bounds)]
28impl<D: InternalStringType> JsStringBuilder<D> {
29    const DATA_SIZE: usize = size_of::<D::Byte>();
30    const MIN_NON_ZERO_CAP: usize = 8 / Self::DATA_SIZE;
31
32    /// Create a new `JsStringBuilder` with capacity of zero.
33    #[inline]
34    #[must_use]
35    pub const fn new() -> Self {
36        Self {
37            cap: 0,
38            len: 0,
39            inner: NonNull::dangling(),
40            phantom_data: PhantomData,
41        }
42    }
43
44    /// Returns the number of elements that inner `RawJsString` holds.
45    #[inline]
46    #[must_use]
47    pub const fn len(&self) -> usize {
48        self.len
49    }
50
51    /// Forces the length of the [`JsStringBuilder`] to `new_len`.
52    ///
53    /// # Safety
54    ///
55    /// - `new_len` must be less than or equal to `capacity()`.
56    /// - The elements at `old_len..new_len` must be initialized.
57    #[inline]
58    pub const unsafe fn set_len(&mut self, new_len: usize) {
59        debug_assert!(new_len <= self.capacity());
60
61        self.len = new_len;
62    }
63
64    /// Returns the total number of elements can hold without reallocating
65    #[inline]
66    #[must_use]
67    pub const fn capacity(&self) -> usize {
68        self.cap
69    }
70
71    /// Returns the capacity calculated from given layout.
72    #[must_use]
73    const fn capacity_from_layout(layout: Layout) -> usize {
74        (layout.size() - D::DATA_OFFSET) / Self::DATA_SIZE
75    }
76
77    /// Create a new `JsStringBuilder` with specific capacity
78    #[inline]
79    #[must_use]
80    pub fn with_capacity(cap: usize) -> Self {
81        if cap == 0 {
82            return Self::new();
83        }
84        let layout = Self::new_layout(cap);
85        #[allow(clippy::cast_ptr_alignment)]
86        // SAFETY:
87        // The layout size of `RawJsString` is never zero, since it has to store
88        // the length of the string and the reference count.
89        let ptr = unsafe { alloc(layout) };
90
91        let Some(ptr) = NonNull::new(ptr.cast()) else {
92            std::alloc::handle_alloc_error(layout)
93        };
94        Self {
95            cap: Self::capacity_from_layout(layout),
96            len: 0,
97            inner: ptr,
98            phantom_data: PhantomData,
99        }
100    }
101
102    /// Checks if the inner `RawJsString` is allocated.
103    #[must_use]
104    fn is_allocated(&self) -> bool {
105        self.inner != NonNull::dangling()
106    }
107
108    /// Returns the inner sequence string's layout.
109    ///
110    /// # Safety
111    ///
112    /// Caller should ensure that the inner is allocated.
113    #[must_use]
114    unsafe fn current_layout(&self) -> Layout {
115        // SAFETY:
116        // 1. Caller should ensure that the inner is allocated.
117        // 2. `unwrap_unchecked` is safe because this layout was successfully
118        //    allocated previously with the same capacity, so it cannot overflow.
119        unsafe {
120            Layout::for_value(self.inner.as_ref())
121                .extend(Layout::array::<D::Byte>(self.capacity()).unwrap_unchecked())
122                .unwrap_unchecked()
123                .0
124                .pad_to_align()
125        }
126    }
127
128    /// Returns the pointer of `data` of inner.
129    ///
130    /// # Safety
131    ///
132    /// Caller should ensure that the inner is allocated.
133    #[must_use]
134    const unsafe fn data(&self) -> *mut D::Byte {
135        let seq_ptr: *mut D::Byte = self.inner.as_ptr().cast();
136        // SAFETY: Caller should ensure that the inner is allocated.
137        unsafe { seq_ptr.byte_add(D::DATA_OFFSET) }
138    }
139
140    /// Allocates when there is not sufficient capacity.
141    #[allow(clippy::inline_always)]
142    #[inline(always)]
143    fn allocate_if_needed(&mut self, required_cap: usize) {
144        if required_cap > self.capacity() {
145            self.allocate(required_cap);
146        }
147    }
148
149    /// Inner logic of `allocate`.
150    ///
151    /// Use `realloc` here because it has a better performance than using combination of `alloc`, `copy` and `dealloc`.
152    #[allow(clippy::cast_ptr_alignment)]
153    fn allocate_inner(&mut self, new_layout: Layout) {
154        let new_ptr = if self.is_allocated() {
155            let old_ptr = self.inner.as_ptr();
156            // SAFETY:
157            // Allocation check has been made above.
158            let old_layout = unsafe { self.current_layout() };
159            // SAFETY:
160            // Valid pointer is required by `realloc` and pointer is checked above to be valid.
161            // The layout size of the sequence string is never zero, since it has to store
162            // the length of the string and the reference count.
163            unsafe { realloc(old_ptr.cast(), old_layout, new_layout.size()) }
164        } else {
165            // SAFETY:
166            // The layout size of the sequence string is never zero, since it has to store
167            // the length of the string and the reference count.
168            unsafe { alloc(new_layout) }
169        };
170
171        let Some(new_ptr) = NonNull::new(new_ptr.cast::<SequenceString<D>>()) else {
172            std::alloc::handle_alloc_error(new_layout)
173        };
174        self.inner = new_ptr;
175        self.cap = Self::capacity_from_layout(new_layout);
176    }
177
178    /// Appends an element to the inner `RawJsString` of `JsStringBuilder`.
179    #[inline]
180    pub fn push(&mut self, v: D::Byte) {
181        let required_cap = self.len() + 1;
182        self.allocate_if_needed(required_cap);
183        // SAFETY:
184        // Capacity has been expanded to be large enough to hold elements.
185        unsafe {
186            self.push_unchecked(v);
187        }
188    }
189
190    /// Pushes elements from slice to `JsStringBuilder` without doing capacity check.
191    ///
192    /// Unlike the standard vector, our held element types are only `u8` and `u16`, which is [`Copy`] derived,
193    ///
194    /// so we only need to copy them instead of cloning.
195    ///
196    /// # Safety
197    ///
198    /// Caller should ensure the capacity is large enough to hold elements.
199    #[inline]
200    pub const unsafe fn extend_from_slice_unchecked(&mut self, v: &[D::Byte]) {
201        // SAFETY:
202        // 1. Caller must ensure `self.len() + v.len() <= self.capacity()` so the destination pointer is in-bounds.
203        // 2. Pointers are aligned: `v` is aligned by Rust's slice guarantee; `self.data()` is aligned because the allocation layout was padded to `D::Byte`'s alignment.
204        // 3. Regions do not overlap because `v` is an immutable reference and `self` is an exclusive mutable reference.
205        unsafe {
206            ptr::copy_nonoverlapping(v.as_ptr(), self.data().add(self.len()), v.len());
207        }
208        self.len += v.len();
209    }
210
211    /// Pushes elements from slice to `JsStringBuilder`.
212    #[inline]
213    pub fn extend_from_slice(&mut self, v: &[D::Byte]) {
214        let required_cap = self.len() + v.len();
215        self.allocate_if_needed(required_cap);
216        // SAFETY:
217        // Capacity has been expanded to be large enough to hold elements.
218        unsafe {
219            self.extend_from_slice_unchecked(v);
220        }
221    }
222
223    fn new_layout(cap: usize) -> Layout {
224        let new_layout = Layout::array::<D::Byte>(cap)
225            .and_then(|arr| Layout::new::<SequenceString<D>>().extend(arr))
226            .map(|(layout, offset)| (layout.pad_to_align(), offset))
227            .map_err(|_| None);
228        match new_layout {
229            Ok((new_layout, offset)) => {
230                debug_assert_eq!(offset, D::DATA_OFFSET);
231                new_layout
232            }
233            Err(None) => alloc_overflow(),
234            Err(Some(layout)) => std::alloc::handle_alloc_error(layout),
235        }
236    }
237
238    /// Similar to [`Vec::reserve`]
239    ///
240    /// Reserves capacity for at least `additional` more elements to be inserted
241    /// in the given `JsStringBuilder<D>`. The collection may reserve more space to
242    /// speculatively avoid frequent reallocations. After calling `reserve`,
243    /// capacity will be greater than or equal to `self.len() + additional`.
244    /// Does nothing if capacity is already sufficient.
245    #[inline]
246    pub fn reserve(&mut self, additional: usize) {
247        if additional > self.capacity().wrapping_sub(self.len) {
248            let Some(cap) = self.len().checked_add(additional) else {
249                alloc_overflow()
250            };
251            self.allocate(cap);
252        }
253    }
254
255    /// Similar to [`Vec::reserve_exact`]
256    ///
257    /// Reserves the minimum capacity for at least `additional` more elements to
258    /// be inserted in the given `JsStringBuilder<D>`. Unlike [`reserve`], this will not
259    /// deliberately over-allocate to speculatively avoid frequent allocations.
260    /// After calling `reserve_exact`, capacity will be greater than or equal to
261    /// `self.len() + additional`. Does nothing if the capacity is already
262    /// sufficient.
263    ///
264    /// Note that the allocator may give the collection more space than it
265    /// requests. Therefore, capacity can not be relied upon to be precisely
266    /// minimal. Prefer [`reserve`] if future insertions are expected.
267    ///
268    /// [`reserve`]: JsStringBuilder::reserve
269    #[inline]
270    pub fn reserve_exact(&mut self, additional: usize) {
271        if additional > self.capacity().wrapping_sub(self.len) {
272            let Some(cap) = self.len().checked_add(additional) else {
273                alloc_overflow()
274            };
275            self.allocate_inner(Self::new_layout(cap));
276        }
277    }
278
279    /// Allocates memory to the inner `RawJsString` by the given capacity.
280    /// Capacity calculation is from [`Vec::reserve`].
281    fn allocate(&mut self, cap: usize) {
282        let cap = std::cmp::max(self.capacity() * 2, cap);
283        let cap = std::cmp::max(Self::MIN_NON_ZERO_CAP, cap);
284        self.allocate_inner(Self::new_layout(cap));
285    }
286
287    /// Appends an element to the inner `RawJsString` of `JsStringBuilder` without doing bounds check.
288    /// # Safety
289    ///
290    /// Caller should ensure the capacity is large enough to hold elements.
291    #[inline]
292    pub const unsafe fn push_unchecked(&mut self, v: D::Byte) {
293        // SAFETY: Caller should ensure the capacity is large enough to hold elements.
294        unsafe {
295            self.data().add(self.len()).write(v);
296            self.len += 1;
297        }
298    }
299
300    /// Returns true if this `JsStringBuilder` has a length of zero, and false otherwise.
301    #[inline]
302    #[must_use]
303    pub fn is_empty(&self) -> bool {
304        self.len() == 0
305    }
306
307    /// Extracts a slice containing the elements in the inner `RawJsString`.
308    #[inline]
309    #[must_use]
310    pub fn as_slice(&self) -> &[D::Byte] {
311        if self.is_allocated() {
312            // SAFETY:
313            // The inner `RawJsString` is allocated which means it is not null.
314            unsafe { std::slice::from_raw_parts(self.data(), self.len()) }
315        } else {
316            &[]
317        }
318    }
319
320    /// Extracts a mutable slice containing the elements in the inner `RawJsString`.
321    ///
322    /// # Safety
323    /// The caller must ensure that the content of the slice is valid encoding before the borrow ends.
324    /// Use of a builder whose contents are not valid encoding is undefined behavior.
325    #[inline]
326    #[must_use]
327    pub unsafe fn as_mut_slice(&mut self) -> &mut [D::Byte] {
328        if self.is_allocated() {
329            // SAFETY:
330            // The inner `RawJsString` is allocated which means it is not null.
331            unsafe { std::slice::from_raw_parts_mut(self.data(), self.len()) }
332        } else {
333            &mut []
334        }
335    }
336
337    /// Builds `JsString` from `JsStringBuilder`
338    #[inline]
339    #[must_use]
340    fn build_inner(mut self) -> JsString {
341        if self.is_empty() {
342            return JsString::default();
343        }
344        let len = self.len();
345
346        // Shrink to fit the length.
347        if len != self.capacity() {
348            let layout = Self::new_layout(self.len());
349            self.allocate_inner(layout);
350        }
351
352        let inner = self.inner;
353
354        // SAFETY:
355        // `NonNull` verified for us that the pointer returned by `alloc` is valid,
356        // meaning we can write to its pointed memory.
357        unsafe {
358            inner.as_ptr().write(SequenceString::<D>::new(len));
359        }
360
361        // Tell the compiler not to call the destructor of `JsStringBuilder`,
362        // because we move inner sequence string to `JsString`.
363        std::mem::forget(self);
364
365        JsString { ptr: inner.cast() }
366    }
367}
368
369impl<D: InternalStringType> Drop for JsStringBuilder<D> {
370    /// Set cold since [`JsStringBuilder`] should be created to build `JsString`
371    #[cold]
372    #[inline]
373    fn drop(&mut self) {
374        if self.is_allocated() {
375            // SAFETY:
376            // Allocation check has been made above.
377            let layout = unsafe { self.current_layout() };
378            // SAFETY:
379            // layout: All the checks for the validity of the layout have already been made on `allocate_inner`.
380            // `NonNull` verified for us that the pointer returned by `alloc` is valid,
381            // meaning we can free its pointed memory.
382            unsafe {
383                dealloc(self.inner.as_ptr().cast(), layout);
384            }
385        }
386    }
387}
388
389impl<D: InternalStringType> AddAssign<&JsStringBuilder<D>> for JsStringBuilder<D> {
390    #[inline]
391    fn add_assign(&mut self, rhs: &JsStringBuilder<D>) {
392        self.extend_from_slice(rhs.as_slice());
393    }
394}
395
396impl<D: InternalStringType> AddAssign<&[D::Byte]> for JsStringBuilder<D> {
397    #[inline]
398    fn add_assign(&mut self, rhs: &[D::Byte]) {
399        self.extend_from_slice(rhs);
400    }
401}
402
403impl<D: InternalStringType> Add<&JsStringBuilder<D>> for JsStringBuilder<D> {
404    type Output = Self;
405
406    #[inline]
407    fn add(mut self, rhs: &JsStringBuilder<D>) -> Self::Output {
408        self.extend_from_slice(rhs.as_slice());
409        self
410    }
411}
412
413impl<D: InternalStringType> Add<&[D::Byte]> for JsStringBuilder<D> {
414    type Output = Self;
415
416    #[inline]
417    fn add(mut self, rhs: &[D::Byte]) -> Self::Output {
418        self.extend_from_slice(rhs);
419        self
420    }
421}
422
423impl<D: InternalStringType> Extend<D::Byte> for JsStringBuilder<D> {
424    #[inline]
425    fn extend<I: IntoIterator<Item = D::Byte>>(&mut self, iter: I) {
426        let iterator = iter.into_iter();
427        let (lower_bound, _) = iterator.size_hint();
428        let require_cap = self.len() + lower_bound;
429        self.allocate_if_needed(require_cap);
430        iterator.for_each(|c| self.push(c));
431    }
432}
433
434impl<D: InternalStringType> FromIterator<D::Byte> for JsStringBuilder<D> {
435    #[inline]
436    fn from_iter<T: IntoIterator<Item = D::Byte>>(iter: T) -> Self {
437        let mut builder = Self::new();
438        builder.extend(iter);
439        builder
440    }
441}
442
443impl<D: InternalStringType> From<&[D::Byte]> for JsStringBuilder<D> {
444    #[inline]
445    fn from(value: &[D::Byte]) -> Self {
446        let mut builder = Self::with_capacity(value.len());
447        // SAFETY: The capacity is large enough to hold elements.
448        unsafe { builder.extend_from_slice_unchecked(value) };
449        builder
450    }
451}
452
453impl<D: InternalStringType> PartialEq for JsStringBuilder<D>
454where
455    D::Byte: Eq + PartialEq,
456{
457    #[inline]
458    fn eq(&self, other: &Self) -> bool {
459        let slice: &[D::Byte] = self.as_slice();
460        let other_slice: &[D::Byte] = other.as_slice();
461        slice.eq(other_slice)
462    }
463}
464
465impl<D: InternalStringType> Clone for JsStringBuilder<D> {
466    #[inline]
467    fn clone(&self) -> Self {
468        if self.is_allocated() {
469            let mut builder = Self::with_capacity(self.capacity());
470            // SAFETY: The capacity is large enough to hold elements.
471            unsafe { builder.extend_from_slice_unchecked(self.as_slice()) };
472            builder
473        } else {
474            Self::new()
475        }
476    }
477
478    /// Performs copy-assignment from `source`.
479    ///
480    /// Rewritten to avoid unnecessary allocation.
481    #[inline]
482    fn clone_from(&mut self, source: &Self) {
483        let source_len = source.len();
484
485        if source_len > self.capacity() {
486            self.allocate(source_len);
487        } else {
488            // At this point, inner sequence string of self or source can be not allocated,
489            // returns earlier to avoid copying from/to `null`.
490            if source_len == 0 {
491                // SAFETY: 0 is always less or equal to self's capacity.
492                unsafe { self.set_len(0) };
493                return;
494            }
495        }
496
497        // SAFETY: self should be allocated after allocation.
498        let self_data = unsafe { self.data() };
499
500        // SAFETY: source_len is greater than 0 so source should be allocated.
501        let source_data = unsafe { source.data() };
502
503        // SAFETY: Borrow checker should not allow this to be overlapped and pointers are valid.
504        unsafe { ptr::copy_nonoverlapping(source_data, self_data, source_len) };
505
506        // SAFETY: source_len has checked to be less or equal to self's capacity.
507        unsafe { self.set_len(source_len) };
508    }
509}
510
511impl JsStringBuilder<Latin1> {
512    /// Checks if all bytes in inner `RawJsString`'s data are ascii.
513    #[inline]
514    #[must_use]
515    pub fn is_ascii(&self) -> bool {
516        self.as_slice().is_ascii()
517    }
518}
519
520impl JsStringBuilder<Utf16> {
521    /// Checks if all u16 in inner `RawJsString`'s data are ascii (<= 0x7F).
522    #[inline]
523    #[must_use]
524    pub fn is_ascii(&self) -> bool {
525        self.as_slice().iter().all(|&c| c <= 0x7F)
526    }
527}
528
529/// **`Latin1`** encoded `JsStringBuilder`
530/// # Warning
531/// If you are not sure the characters that will be added and don't want to preprocess them,
532/// use [`CommonJsStringBuilder`] instead.
533/// ## Examples
534///
535/// ```rust
536/// use boa_string::Latin1JsStringBuilder;
537/// let mut s = Latin1JsStringBuilder::new();
538/// s.push(b'x');
539/// s.extend_from_slice(&[b'1', b'2', b'3']);
540/// s.extend([b'1', b'2', b'3']);
541/// let js_string = s.build();
542/// ```
543pub type Latin1JsStringBuilder = JsStringBuilder<Latin1>;
544
545impl Latin1JsStringBuilder {
546    /// Builds a `JsString` if the current instance is strictly `ASCII`.
547    ///
548    /// When the string contains characters outside the `ASCII` range, it cannot be determined
549    /// whether the encoding is `Latin1` or others. Therefore, this method only returns a
550    /// valid `JsString` when the instance is entirely `ASCII`. If any non-`ASCII` characters
551    /// are present, it returns `None` to avoid ambiguity in encoding.
552    ///
553    /// If the caller is certain that the string is encoded in `Latin1`,
554    /// [`build_as_latin1`](Self::build_as_latin1) can be used to avoid the `ASCII` check.
555    #[inline]
556    #[must_use]
557    pub fn build(self) -> Option<JsString> {
558        if self.is_ascii() {
559            Some(self.build_inner())
560        } else {
561            None
562        }
563    }
564
565    /// Builds `JsString` from `Latin1JsStringBuilder`, assume that the inner data is `Latin1` encoded
566    ///
567    /// # Safety
568    /// Caller must ensure that the string is encoded in `Latin1`.
569    ///
570    /// If the string contains characters outside the `Latin1` range, it may lead to encoding errors,
571    /// resulting in an incorrect or malformed `JsString`. This could cause undefined behavior
572    /// when the resulting string is used in further operations or when interfacing with other
573    /// parts of the system that expect valid `Latin1` encoded string.
574    #[inline]
575    #[must_use]
576    pub unsafe fn build_as_latin1(self) -> JsString {
577        self.build_inner()
578    }
579}
580
581/// **`UTF-16`** encoded `JsStringBuilder`
582/// ## Examples
583///
584/// ```rust
585/// use boa_string::Utf16JsStringBuilder;
586/// let mut s = Utf16JsStringBuilder::new();
587/// s.push(b'x' as u16);
588/// s.extend_from_slice(&[b'1', b'2', b'3'].map(u16::from));
589/// s.extend([0xD83C, 0xDFB9, 0xD83C, 0xDFB6, 0xD83C, 0xDFB5]); // 🎹🎢🎡
590/// let js_string = s.build();
591/// ```
592pub type Utf16JsStringBuilder = JsStringBuilder<Utf16>;
593
594impl Utf16JsStringBuilder {
595    /// Builds `JsString` from `Utf16JsStringBuilder`
596    #[inline]
597    #[must_use]
598    pub fn build(self) -> JsString {
599        self.build_inner()
600    }
601}
602
603/// Represents a segment of a string used to construct a [`JsString`].
604#[derive(Clone, Debug)]
605pub enum Segment<'a> {
606    /// A string segment represented as a `JsString`.
607    String(JsString),
608
609    /// A string segment represented as a `JsStr`.
610    Str(JsStr<'a>),
611
612    /// A string segment represented as a byte.
613    Latin1(u8),
614
615    /// A Unicode code point segment represented as a character.
616    CodePoint(char),
617}
618
619impl Segment<'_> {
620    /// Checks if the segment can be represented as `Latin1` characters.
621    #[inline]
622    #[must_use]
623    fn can_be_latin1(&self) -> bool {
624        match self {
625            Segment::String(s) => s.as_str().is_latin1(),
626            Segment::Str(s) => s.is_latin1(),
627            Segment::Latin1(_) => true,
628            Segment::CodePoint(ch) => *ch as u32 <= 0xFF,
629        }
630    }
631}
632
633impl From<JsString> for Segment<'_> {
634    #[inline]
635    fn from(value: JsString) -> Self {
636        Self::String(value)
637    }
638}
639
640impl From<String> for Segment<'_> {
641    #[inline]
642    fn from(value: String) -> Self {
643        Self::String(value.into())
644    }
645}
646
647impl From<&[u16]> for Segment<'_> {
648    #[inline]
649    fn from(value: &[u16]) -> Self {
650        Self::String(value.into())
651    }
652}
653
654impl From<&str> for Segment<'_> {
655    #[inline]
656    fn from(value: &str) -> Self {
657        Self::String(value.into())
658    }
659}
660
661impl<'seg, 'ref_str: 'seg> From<JsStr<'ref_str>> for Segment<'seg> {
662    #[inline]
663    fn from(value: JsStr<'ref_str>) -> Self {
664        Self::Str(value)
665    }
666}
667
668impl From<u8> for Segment<'_> {
669    #[inline]
670    fn from(value: u8) -> Self {
671        Self::Latin1(value)
672    }
673}
674
675impl From<char> for Segment<'_> {
676    #[inline]
677    fn from(value: char) -> Self {
678        Self::CodePoint(value)
679    }
680}
681
682/// Common `JsString` builder that accepts multiple variant of string or character.
683///
684/// Originally based on [kiesel-js](https://codeberg.org/kiesel-js/kiesel/src/branch/main/src/types/language/String/Builder.zig)
685#[derive(Clone, Debug, Default)]
686pub struct CommonJsStringBuilder<'a> {
687    segments: Vec<Segment<'a>>,
688}
689
690impl<'seg, 'ref_str: 'seg> CommonJsStringBuilder<'seg> {
691    /// Creates a new `CommonJsStringBuilder` with capacity of zero.
692    #[inline]
693    #[must_use]
694    pub const fn new() -> Self {
695        Self {
696            segments: Vec::new(),
697        }
698    }
699
700    /// Similar to `Vec::with_capacity`.
701    ///
702    /// Creates a new `CommonJsStringBuilder` with given capacity.
703    #[inline]
704    #[must_use]
705    pub fn with_capacity(capacity: usize) -> Self {
706        Self {
707            segments: Vec::with_capacity(capacity),
708        }
709    }
710
711    /// Similar to `Vec::reserve`.
712    ///
713    /// Reserves additional capacity for the inner vector.
714    #[inline]
715    pub fn reserve(&mut self, additional: usize) {
716        self.segments.reserve(additional);
717    }
718
719    /// Similar to `Vec::reserve_exact`.
720    ///
721    /// Reserves the minimum capacity for the inner vector.
722    #[inline]
723    pub fn reserve_exact(&mut self, additional: usize) {
724        self.segments.reserve_exact(additional);
725    }
726
727    /// Appends string segments to the back of the inner vector.
728    #[inline]
729    pub fn push<T: Into<Segment<'ref_str>>>(&mut self, seg: T) {
730        self.segments.push(seg.into());
731    }
732
733    /// Checks if all string segments can be represented as `Latin1` characters.
734    #[inline]
735    #[must_use]
736    pub fn can_be_latin1(&self) -> bool {
737        self.segments.iter().all(Segment::can_be_latin1)
738    }
739
740    /// Returns the number of string segment in inner vector.
741    #[inline]
742    #[must_use]
743    pub fn len(&self) -> usize {
744        self.segments.len()
745    }
746
747    /// Returns true if this `CommonJsStringBuilder` has a length of zero, and false otherwise.
748    #[inline]
749    #[must_use]
750    pub fn is_empty(&self) -> bool {
751        self.len() == 0
752    }
753
754    /// Builds `Latin1` encoded `JsString` from string segments.
755    ///
756    /// This doesn't consume the builder itself because it may fails to build
757    /// and the caller may wants to keep the builder for further operations.
758    ///
759    /// This processes the following types of segments:
760    ///
761    /// - `Segment::String(s)`: Encodes the string if it can be represented in `Latin1`.
762    /// - `Segment::Str(s)`: Encodes the string slice if it can be represented in `Latin1`.
763    /// - `Segment::Latin1(b)`: Encodes the byte if it's within the `ASCII` range.
764    /// - `Segment::CodePoint(ch)`: Encodes the code point by converting it to a byte if it's within the `ASCII` range.
765    ///
766    /// Return `None` if any segment fails to encode.
767    #[inline]
768    #[must_use]
769    #[allow(clippy::cast_lossless)]
770    pub fn build_from_latin1(&self) -> Option<JsString> {
771        let mut builder = Latin1JsStringBuilder::new();
772        for seg in &self.segments {
773            match seg {
774                Segment::String(s) => {
775                    builder.extend_from_slice(s.as_str().as_latin1()?);
776                }
777                Segment::Str(s) => {
778                    builder.extend_from_slice(s.as_latin1()?);
779                }
780                Segment::Latin1(b) => {
781                    if *b <= 0x7f {
782                        builder.push(*b);
783                    } else {
784                        return None;
785                    }
786                }
787                Segment::CodePoint(ch) => {
788                    if let Ok(b) = u8::try_from(*ch as u32) {
789                        builder.push(b);
790                    } else {
791                        return None;
792                    }
793                }
794            }
795        }
796        builder.build()
797    }
798
799    /// Builds `Utf-16` encoded `JsString` from string segments.
800    #[inline]
801    #[must_use]
802    #[allow(clippy::cast_possible_truncation)]
803    pub fn build_from_utf16(self) -> JsString {
804        let mut builder = Utf16JsStringBuilder::new();
805        for seg in self.segments {
806            match seg {
807                Segment::String(s) => {
808                    let js_str = s.as_str();
809                    match js_str.variant() {
810                        JsStrVariant::Latin1(s) => builder.extend(s.iter().copied().map(u16::from)),
811                        JsStrVariant::Utf16(s) => builder.extend_from_slice(s),
812                    }
813                }
814                Segment::Str(s) => match s.variant() {
815                    JsStrVariant::Latin1(s) => builder.extend(s.iter().copied().map(u16::from)),
816                    JsStrVariant::Utf16(s) => builder.extend_from_slice(s),
817                },
818                Segment::Latin1(latin1) => builder.push(u16::from(latin1)),
819                Segment::CodePoint(code_point) => {
820                    builder.extend_from_slice(code_point.encode_utf16(&mut [0_u16; 2]));
821                }
822            }
823        }
824        builder.build()
825    }
826
827    /// Builds `JsString` from `CommonJsStringBuilder`,
828    ///
829    /// This function first checks if the instance is empty:
830    /// - If it is empty, it returns the default `JsString`.
831    /// - If it can be represented as Latin1 characters, it safely encodes it as `Latin1`.
832    /// - Otherwise, it falls back to encoding using `UTF-16`.
833    #[inline]
834    #[must_use]
835    pub fn build(self) -> JsString {
836        if self.is_empty() {
837            JsString::default()
838        } else if self.can_be_latin1() {
839            // SAFETY:
840            // All string segments can be represented as Latin1, so this can be encoded as `Latin1`.
841            unsafe { self.build_as_latin1() }
842        } else {
843            self.build_from_utf16()
844        }
845    }
846
847    /// Builds `Latin1` encoded `JsString` from `CommonJsStringBuilder`, return `None` if segments can't be encoded as `Latin1`
848    ///
849    /// # Safety
850    /// Caller must ensure that the string segments can be `Latin1` encoded.
851    ///
852    /// If string segments can't be `Latin1` encoded, it may lead to encoding errors,
853    /// resulting in an incorrect or malformed `JsString`. This could cause undefined behavior
854    /// when the resulting string is used in further operations or when interfacing with other
855    /// parts of the system that expect valid `Latin1` encoded string.
856    #[inline]
857    #[must_use]
858    pub unsafe fn build_as_latin1(self) -> JsString {
859        let mut builder = Latin1JsStringBuilder::new();
860        for seg in self.segments {
861            match seg {
862                Segment::String(s) => {
863                    let js_str = s.as_str();
864                    let Some(s) = js_str.as_latin1() else {
865                        unreachable!("string segment should be latin1")
866                    };
867                    builder.extend_from_slice(s);
868                }
869                Segment::Str(s) => {
870                    let Some(s) = s.as_latin1() else {
871                        unreachable!("string segment should be latin1")
872                    };
873                    builder.extend_from_slice(s);
874                }
875                Segment::Latin1(latin1) => builder.push(latin1),
876                Segment::CodePoint(code_point) => builder.push(code_point as u8),
877            }
878        }
879        // SAFETY: All string segments can be encoded as `Latin1` string.
880        unsafe { builder.build_as_latin1() }
881    }
882}
883
884impl<'ref_str, T: Into<Segment<'ref_str>>> AddAssign<T> for CommonJsStringBuilder<'ref_str> {
885    #[inline]
886    fn add_assign(&mut self, rhs: T) {
887        self.push(rhs);
888    }
889}
890
891impl<'ref_str, T: Into<Segment<'ref_str>>> Add<T> for CommonJsStringBuilder<'ref_str> {
892    type Output = Self;
893
894    #[inline]
895    fn add(mut self, rhs: T) -> Self::Output {
896        self.push(rhs);
897        self
898    }
899}