@@ -457,12 +457,61 @@ private void ReadHistoryFile()
457
457
{
458
458
WithHistoryFileMutexDo ( 1000 , ( ) =>
459
459
{
460
- var historyLines = File . ReadAllLines ( Options . HistorySavePath ) ;
460
+ var historyLines = ReadHistoryLinesImpl ( Options . HistorySavePath , Options . MaximumHistoryCount ) ;
461
461
UpdateHistoryFromFile ( historyLines , fromDifferentSession : false , fromInitialRead : true ) ;
462
462
var fileInfo = new FileInfo ( Options . HistorySavePath ) ;
463
463
_historyFileLastSavedSize = fileInfo . Length ;
464
464
} ) ;
465
465
}
466
+
467
+ static IEnumerable < string > ReadHistoryLinesImpl ( string path , int historyCount )
468
+ {
469
+ const long offset_1mb = 1048576 ;
470
+ const long offset_05mb = 524288 ;
471
+
472
+ // 1mb content contains more than 34,000 history lines for a typical usage, which should be
473
+ // more than enough to cover 20,000 history records (a history record could be a multi-line
474
+ // command). Similarly, 0.5mb content should be enough to cover 10,000 history records.
475
+ // We optimize the file reading when the history count falls in those ranges. If the history
476
+ // count is even larger, which should be very rare, we just read all lines.
477
+ long offset = historyCount switch
478
+ {
479
+ <= 10000 => offset_05mb ,
480
+ <= 20000 => offset_1mb ,
481
+ _ => 0 ,
482
+ } ;
483
+
484
+ using var fs = new FileStream ( path , FileMode . Open ) ;
485
+ using var sr = new StreamReader ( fs ) ;
486
+
487
+ if ( offset > 0 && fs . Length > offset )
488
+ {
489
+ // When the file size is larger than the offset, we only read that amount of content from the end.
490
+ fs . Seek ( - offset , SeekOrigin . End ) ;
491
+
492
+ // After seeking, the current position may point at the middle of a history record, or even at a
493
+ // byte within a UTF-8 character (history file is saved with UTF-8 encoding). So, let's ignore the
494
+ // first line read from that position.
495
+ sr . ReadLine ( ) ;
496
+
497
+ string line ;
498
+ while ( ( line = sr . ReadLine ( ) ) is not null )
499
+ {
500
+ if ( ! line . EndsWith ( "`" , StringComparison . Ordinal ) )
501
+ {
502
+ // A complete history record is guaranteed to start from the next line.
503
+ break ;
504
+ }
505
+ }
506
+ }
507
+
508
+ // Read lines in the streaming way, so it won't consume to much memory even if we have to
509
+ // read all lines from a large history file.
510
+ while ( ! sr . EndOfStream )
511
+ {
512
+ yield return sr . ReadLine ( ) ;
513
+ }
514
+ }
466
515
}
467
516
468
517
void UpdateHistoryFromFile ( IEnumerable < string > historyLines , bool fromDifferentSession , bool fromInitialRead )
0 commit comments