@@ -34,6 +34,7 @@ use std::collections::HashMap;
34
34
use std:: convert:: Infallible ;
35
35
use std:: fmt:: { Debug , Display , Formatter , Write } ;
36
36
use std:: io:: { self , BufRead , BufReader , ErrorKind , Write as IoWrite } ;
37
+ use std:: num:: NonZeroUsize ;
37
38
use std:: ops:: Deref ;
38
39
use std:: path:: PathBuf ;
39
40
use std:: str:: FromStr ;
@@ -430,11 +431,13 @@ impl OpCase {
430
431
}
431
432
}
432
433
433
- #[ derive( Debug , Clone ) ]
434
+ // IMPORTANT: This struct MUST remain `Copy` to ensure that
435
+ // there are no heap allocations when performing marker operations.
436
+ #[ derive( Debug , Clone , Copy ) ]
434
437
struct BufferState {
435
438
op_case : OpCase ,
436
439
row_count : usize ,
437
- first_table : Option < String > ,
440
+ first_table_len : Option < NonZeroUsize > ,
438
441
transactional : bool ,
439
442
}
440
443
@@ -443,17 +446,10 @@ impl BufferState {
443
446
Self {
444
447
op_case : OpCase :: Init ,
445
448
row_count : 0 ,
446
- first_table : None ,
449
+ first_table_len : None ,
447
450
transactional : true ,
448
451
}
449
452
}
450
-
451
- fn clear ( & mut self ) {
452
- self . op_case = OpCase :: Init ;
453
- self . row_count = 0 ;
454
- self . first_table = None ;
455
- self . transactional = true ;
456
- }
457
453
}
458
454
459
455
/// A reusable buffer to prepare a batch of ILP messages.
@@ -494,11 +490,11 @@ impl BufferState {
494
490
/// * A row always starts with [`table`](Buffer::table).
495
491
/// * A row must contain at least one [`symbol`](Buffer::symbol) or
496
492
/// column (
497
- /// [`column_bool`](Buffer::column_bool),
498
- /// [`column_i64`](Buffer::column_i64),
499
- /// [`column_f64`](Buffer::column_f64),
500
- /// [`column_str`](Buffer::column_str),
501
- /// [`column_ts`](Buffer::column_ts)).
493
+ /// [`column_bool`](Buffer::column_bool),
494
+ /// [`column_i64`](Buffer::column_i64),
495
+ /// [`column_f64`](Buffer::column_f64),
496
+ /// [`column_str`](Buffer::column_str),
497
+ /// [`column_ts`](Buffer::column_ts)).
502
498
/// * Symbols must appear before columns.
503
499
/// * A row must be terminated with either [`at`](Buffer::at) or
504
500
/// [`at_now`](Buffer::at_now).
@@ -630,7 +626,7 @@ impl Buffer {
630
626
)
631
627
) ) ;
632
628
}
633
- self . marker = Some ( ( self . output . len ( ) , self . state . clone ( ) ) ) ;
629
+ self . marker = Some ( ( self . output . len ( ) , self . state ) ) ;
634
630
Ok ( ( ) )
635
631
}
636
632
@@ -663,7 +659,7 @@ impl Buffer {
663
659
/// [`capacity`](Buffer::capacity).
664
660
pub fn clear ( & mut self ) {
665
661
self . output . clear ( ) ;
666
- self . state . clear ( ) ;
662
+ self . state = BufferState :: new ( ) ;
667
663
self . marker = None ;
668
664
}
669
665
@@ -729,16 +725,31 @@ impl Buffer {
729
725
let name: TableName < ' a > = name. try_into ( ) ?;
730
726
self . validate_max_name_len ( name. name ) ?;
731
727
self . check_op ( Op :: Table ) ?;
728
+ let table_begin = self . output . len ( ) ;
732
729
write_escaped_unquoted ( & mut self . output , name. name ) ;
730
+ let table_end = self . output . len ( ) ;
733
731
self . state . op_case = OpCase :: TableWritten ;
734
732
735
733
// A buffer stops being transactional if it targets multiple tables.
736
- if let Some ( first_table) = & self . state . first_table {
737
- if first_table != name. name {
734
+ if let Some ( first_table_len) = & self . state . first_table_len {
735
+ let first_table = & self . output [ 0 ..( first_table_len. get ( ) ) ] ;
736
+ let this_table = & self . output [ table_begin..table_end] ;
737
+ if first_table != this_table {
738
738
self . state . transactional = false ;
739
739
}
740
740
} else {
741
- self . state . first_table = Some ( name. name . to_owned ( ) ) ;
741
+ debug_assert ! ( table_begin == 0 ) ;
742
+
743
+ // This is a bit confusing, so worth explaining:
744
+ // `NonZeroUsize::new(table_end)` will return `None` if `table_end` is 0,
745
+ // but we know that `table_end` is never 0 here, we just need an option type
746
+ // anyway, so we don't bother unwrapping it to then wrap it again.
747
+ let first_table_len = NonZeroUsize :: new ( table_end) ;
748
+
749
+ // Instead we just assert that it's `Some`.
750
+ debug_assert ! ( first_table_len. is_some( ) ) ;
751
+
752
+ self . state . first_table_len = first_table_len;
742
753
}
743
754
Ok ( self )
744
755
}
0 commit comments