11//! A string builder for constructing source code.
22
3- use std:: iter;
3+ use std:: { iter, ptr } ;
44
55use crate :: assert_unchecked;
66
@@ -287,6 +287,43 @@ impl CodeBuffer {
287287 }
288288 }
289289
290+ /// Push a byte to the buffer, without checking that there's sufficient capacity, and without checking
291+ /// that the buffer still represents a valid UTF-8 string.
292+ ///
293+ /// Useful for reducing number of bounds checks when pushing multiple batches of bytes/strings.
294+ ///
295+ /// # SAFETY
296+ ///
297+ /// All the safety constraints of [`print_byte_unchecked`] apply.
298+ ///
299+ /// Additionally, caller must ensure buffer is not full to capacity.
300+ /// i.e. `self.capacity() - self.len() >= 1`.
301+ ///
302+ /// # Example
303+ ///
304+ /// ```rs
305+ /// # use oxc_data_structures::code_buffer::CodeBuffer;
306+ /// let mut code = CodeBuffer::new();
307+ /// code.reserve(2);
308+ ///
309+ /// // SAFETY: 'a' and 'b' are a valid ASCII characters.
310+ /// // Their UTF-8 representation only require a single byte.
311+ /// // We reserved 2 bytes capacity above.
312+ /// unsafe {
313+ /// code.print_byte_unchecked_cap(b'a');
314+ /// code.print_byte_unchecked_cap(b'b');
315+ /// }
316+ /// ```
317+ ///
318+ /// [`print_byte_unchecked`]: Self::print_byte_unchecked
319+ #[ inline]
320+ pub unsafe fn print_byte_unchecked_cap ( & mut self , byte : u8 ) {
321+ // SAFETY: Caller guarantees buffer is not full to capacity, and they take responsibility
322+ // for ensuring that buffer is not put into a state where it contains an invalid UTF-8 string.
323+ unsafe { assert_unchecked ! ( self . buf. len( ) != self . buf. capacity( ) ) }
324+ self . buf . push ( byte) ;
325+ }
326+
290327 /// Push a single Unicode character into the buffer.
291328 ///
292329 /// When pushing multiple characters, consider choosing [`print_str`] over this method
@@ -326,6 +363,38 @@ impl CodeBuffer {
326363 self . buf . extend_from_slice ( s. as_ref ( ) . as_bytes ( ) ) ;
327364 }
328365
366+ /// Push a string into the buffer, without checking there is sufficient capacity.
367+ ///
368+ /// Useful for reducing number of bounds checks when pushing multiple batches of bytes/strings.
369+ ///
370+ /// # SAFETY
371+ ///
372+ /// There must be sufficient spare capacity in the buffer for the string `s`.
373+ /// i.e. `self.capacity() - self.len() >= s.len()`.
374+ ///
375+ /// # Example
376+ ///
377+ /// ```rs
378+ /// # use oxc_data_structures::code_buffer::CodeBuffer;
379+ /// let mut code = CodeBuffer::new();
380+ ///
381+ /// let s1 = "abcde";
382+ /// let s2 = "fghij";
383+ ///
384+ /// code.reserve(s1.len() + s2.len());
385+ /// // SAFETY: Have reserved sufficient capacity
386+ /// unsafe {
387+ /// code.print_str_unchecked_cap(s1);
388+ /// code.print_str_unchecked_cap(s2);
389+ /// }
390+ /// ```
391+ #[ inline]
392+ pub unsafe fn print_str_unchecked_cap < S : AsRef < str > > ( & mut self , s : S ) {
393+ let bytes = s. as_ref ( ) . as_bytes ( ) ;
394+ // SAFETY: Caller guarantees there is sufficient capacity in buffer for `s`
395+ unsafe { self . print_bytes_unchecked_cap ( bytes) } ;
396+ }
397+
329398 /// Push a sequence of ASCII characters into the buffer.
330399 ///
331400 /// # Panics
@@ -381,6 +450,50 @@ impl CodeBuffer {
381450 self . buf . extend_from_slice ( bytes) ;
382451 }
383452
453+ /// Print a slice of bytes, without checking there is sufficient capacity,
454+ /// and without checking that the buffer still contains a valid UTF-8 string.
455+ ///
456+ /// Useful for reducing number of bounds checks when pushing multiple batches of bytes/strings.
457+ ///
458+ /// # SAFETY
459+ ///
460+ /// All the safety requirements of [`print_bytes_unchecked`] apply.
461+ ///
462+ /// Additionally, there must be sufficient spare capacity in the buffer for `bytes`.
463+ /// i.e. `self.capacity() - self.len() >= bytes.len()`.
464+ ///
465+ /// # Example
466+ ///
467+ /// ```rs
468+ /// # use oxc_data_structures::code_buffer::CodeBuffer;
469+ /// let mut code = CodeBuffer::new();
470+ ///
471+ /// let b1 = b"abcde";
472+ /// let b2 = b"fghij";
473+ ///
474+ /// code.reserve(b1.len() + b2.len());
475+ /// // SAFETY: Have reserved sufficient capacity.
476+ /// // `b1` and `b2` only contain ASCII bytes.
477+ /// unsafe {
478+ /// code.print_bytes_unchecked_cap(b1);
479+ /// code.print_bytes_unchecked_cap(b2);
480+ /// }
481+ /// ```
482+ ///
483+ /// [`print_bytes_unchecked`]: Self::print_bytes_unchecked
484+ #[ inline]
485+ pub unsafe fn print_bytes_unchecked_cap ( & mut self , bytes : & [ u8 ] ) {
486+ let src = bytes. as_ptr ( ) ;
487+ let buf_len = self . len ( ) ;
488+ let bytes_len = bytes. len ( ) ;
489+ // SAFETY: Caller guarantees there is sufficient capacity in buffer to contain `bytes`
490+ unsafe {
491+ let dst = self . buf . as_mut_ptr ( ) . add ( buf_len) ;
492+ ptr:: copy_nonoverlapping ( src, dst, bytes_len) ;
493+ self . buf . set_len ( buf_len + bytes_len) ;
494+ }
495+ }
496+
384497 /// Print a sequence of bytes, without checking that the buffer still contains a valid UTF-8 string.
385498 ///
386499 /// # SAFETY
@@ -541,6 +654,26 @@ mod test {
541654 assert_eq ! ( source, s) ;
542655 }
543656
657+ #[ test]
658+ fn print_str_unchecked_cap ( ) {
659+ let mut code = CodeBuffer :: new ( ) ;
660+
661+ let s1 = "Hello, " ;
662+ let s2 = "world" ;
663+ let s3 = "!" ;
664+
665+ code. reserve ( s1. len ( ) + s2. len ( ) + s3. len ( ) ) ;
666+ // SAFETY: Have reserved sufficient capacity
667+ unsafe {
668+ code. print_str_unchecked_cap ( s1) ;
669+ code. print_str_unchecked_cap ( s2) ;
670+ code. print_str_unchecked_cap ( s3) ;
671+ }
672+
673+ assert_eq ! ( code. len( ) , s1. len( ) + s2. len( ) + s3. len( ) ) ;
674+ assert_eq ! ( String :: from( code) , "Hello, world!" ) ;
675+ }
676+
544677 #[ test]
545678 #[ expect( clippy:: byte_char_slices) ]
546679 fn print_ascii_byte ( ) {
@@ -570,6 +703,22 @@ mod test {
570703 assert_eq ! ( String :: from( code) , "foo" ) ;
571704 }
572705
706+ #[ test]
707+ #[ expect( clippy:: byte_char_slices) ]
708+ fn print_byte_unchecked_cap ( ) {
709+ let mut code = CodeBuffer :: with_capacity ( 3 ) ;
710+ // SAFETY: These bytes are all ASCII
711+ unsafe {
712+ code. print_byte_unchecked_cap ( b'f' ) ;
713+ code. print_byte_unchecked_cap ( b'o' ) ;
714+ code. print_byte_unchecked_cap ( b'o' ) ;
715+ }
716+
717+ assert_eq ! ( code. len( ) , 3 ) ;
718+ assert_eq ! ( code. as_bytes( ) , & [ b'f' , b'o' , b'o' ] ) ;
719+ assert_eq ! ( String :: from( code) , "foo" ) ;
720+ }
721+
573722 #[ test]
574723 #[ expect( clippy:: byte_char_slices) ]
575724 fn print_bytes_unchecked ( ) {
@@ -582,6 +731,26 @@ mod test {
582731 assert_eq ! ( String :: from( code) , "foo" ) ;
583732 }
584733
734+ #[ test]
735+ fn print_bytes_unchecked_cap ( ) {
736+ let mut code = CodeBuffer :: new ( ) ;
737+
738+ let s1 = b"Hello, " ;
739+ let s2 = b"world" ;
740+ let s3 = b"!" ;
741+
742+ code. reserve ( s1. len ( ) + s2. len ( ) + s3. len ( ) ) ;
743+ // SAFETY: Have reserved sufficient capacity. All bytes are ASCII.
744+ unsafe {
745+ code. print_bytes_unchecked_cap ( s1) ;
746+ code. print_bytes_unchecked_cap ( s2) ;
747+ code. print_bytes_unchecked_cap ( s3) ;
748+ }
749+
750+ assert_eq ! ( code. len( ) , s1. len( ) + s2. len( ) + s3. len( ) ) ;
751+ assert_eq ! ( String :: from( code) , "Hello, world!" ) ;
752+ }
753+
585754 #[ test]
586755 #[ expect( clippy:: byte_char_slices) ]
587756 fn print_bytes_iter_unchecked ( ) {
0 commit comments