55using System . Collections . Concurrent ;
66using System . Collections . Generic ;
77using System . Linq ;
8- #if SYSTEM_TEXT_ENCODINGEXTENSIONS
98using System . Text ;
10- #endif
119using System . Threading ;
1210using System . Threading . Tasks ;
1311
@@ -57,9 +55,9 @@ private sealed class ArrayBufferWriterPool(int initialCapacity)
5755 ) {
5856 }
5957
60- private sealed class StringListPool ( int initialCapacity )
61- : ObjectPool < List < string > > (
62- create : ( ) => new List < string > ( capacity : initialCapacity ) ,
58+ private sealed class StringBuilderPool ( int initialCapacity )
59+ : ObjectPool < StringBuilder > (
60+ create : ( ) => new StringBuilder ( capacity : initialCapacity ) ,
6361 clear : item => item . Clear ( )
6462 ) {
6563 }
@@ -68,7 +66,7 @@ private sealed class StringListPool(int initialCapacity)
6866 private readonly string banner ;
6967 private readonly string versionInformation ;
7068 private readonly ArrayBufferWriterPool bufferWriterPool = new ( initialCapacity : 256 ) ;
71- private readonly StringListPool responseLineListPool = new ( initialCapacity : 32 ) ;
69+ private readonly StringBuilderPool responseBuilderPool = new ( initialCapacity : 512 ) ;
7270 private readonly Dictionary < string , IPlugin > plugins = new ( StringComparer . Ordinal ) ;
7371
7472 /// <summary>
@@ -305,10 +303,6 @@ CancellationToken cancellationToken
305303 }
306304 }
307305
308- #pragma warning disable IDE0230
309- private static readonly ReadOnlyMemory < byte > EndOfLine = new [ ] { ( byte ) '\n ' } ;
310- #pragma warning restore IDE0230
311-
312306 private static readonly string [ ] ResponseLinesUnknownService = [
313307 "# Unknown service" ,
314308 "." ,
@@ -325,7 +319,7 @@ CancellationToken cancellationToken
325319 cancellationToken : cancellationToken
326320 ) ;
327321
328- protected async ValueTask SendResponseAsync (
322+ protected ValueTask SendResponseAsync (
329323 IMuninNodeClient client ,
330324 IEnumerable < string > responseLines ,
331325 CancellationToken cancellationToken
@@ -336,28 +330,65 @@ CancellationToken cancellationToken
336330 if ( responseLines is null )
337331 throw new ArgumentNullException ( nameof ( responseLines ) ) ;
338332
333+ var builder = responseBuilderPool . Take ( ) ;
334+
335+ try {
336+ foreach ( var line in responseLines ) {
337+ builder . Append ( line ) . Append ( '\n ' ) ;
338+ }
339+
340+ return SendResponseAsync ( client , builder , cancellationToken ) ;
341+ }
342+ finally {
343+ responseBuilderPool . Return ( builder ) ;
344+ }
345+ }
346+
347+ private async ValueTask SendResponseAsync (
348+ IMuninNodeClient client ,
349+ StringBuilder responseBuilder ,
350+ CancellationToken cancellationToken
351+ )
352+ {
353+ if ( client is null )
354+ throw new ArgumentNullException ( nameof ( client ) ) ;
355+ if ( responseBuilder is null )
356+ throw new ArgumentNullException ( nameof ( responseBuilder ) ) ;
357+
339358 cancellationToken . ThrowIfCancellationRequested ( ) ;
340359
341360 var writer = bufferWriterPool . Take ( ) ;
342361
343362 try {
344- foreach ( var responseLine in responseLines ) {
363+ #if SYSTEM_TEXT_STRINGBUILDER_GETCHUNKS
364+ foreach ( var chunk in responseBuilder . GetChunks ( ) ) {
345365#if SYSTEM_TEXT_ENCODINGEXTENSIONS
346- _ = profile . Encoding . GetBytes ( responseLine , writer ) ;
366+ _ = profile . Encoding . GetBytes ( chunk . Span , writer ) ;
367+ #else
368+ var byteCount = profile . Encoding . GetByteCount ( chunk ) ;
369+ var buffer = writer . GetMemory ( byteCount ) ;
370+ var bytesWritten = profile . Encoding . GetBytes ( chunk , buffer . Span ) ;
347371
348- writer . Write ( EndOfLine . Span ) ;
372+ writer . Advance ( bytesWritten ) ;
373+ #endif
374+ }
349375#else
350- var totalByteCount = profile . Encoding . GetByteCount ( responseLine ) + EndOfLine . Length ;
351- var buffer = writer . GetMemory ( totalByteCount ) ;
352- var bytesWritten = profile . Encoding . GetBytes ( responseLine , buffer . Span ) ;
376+ var responseChars = ArrayPool < char > . Shared . Rent ( responseBuilder . Length ) ;
353377
354- EndOfLine . CopyTo ( buffer [ bytesWritten ..] ) ;
378+ try {
379+ responseBuilder . CopyTo ( 0 , responseChars , 0 , responseBuilder . Length ) ;
355380
356- bytesWritten += EndOfLine . Length ;
381+ var responseCharsMemory = responseChars . AsMemory ( 0 , responseBuilder . Length ) ;
382+ var byteCount = profile . Encoding . GetByteCount ( responseCharsMemory . Span ) ;
383+ var buffer = writer . GetMemory ( byteCount ) ;
384+ var bytesWritten = profile . Encoding . GetBytes ( responseCharsMemory . Span , buffer . Span ) ;
357385
358386 writer . Advance ( bytesWritten ) ;
359- #endif
360387 }
388+ finally {
389+ ArrayPool < char > . Shared . Return ( responseChars ) ;
390+ }
391+ #endif
361392
362393 await client . SendAsync (
363394 buffer : writer . WrittenMemory ,
@@ -526,45 +557,45 @@ await SendResponseAsync(
526557 return ;
527558 }
528559
529- var responseLines = responseLineListPool . Take ( ) ;
560+ var responseBuilder = responseBuilderPool . Take ( ) ;
530561
531562 try {
532563 if ( plugin is IMultigraphPlugin multigraphPlugin ) {
533564 // 'Protocol extension: multiple graphs from one plugin' (https://guide.munin-monitoring.org/en/latest/plugin/protocol-multigraph.html)
534565 foreach ( var subPlugin in multigraphPlugin . Plugins ) {
535- responseLines . Add ( $ "multigraph { subPlugin . Name } ") ;
566+ responseBuilder . Append ( provider : null , $ "multigraph { subPlugin . Name } \n ") ;
536567
537568 await WriteFetchResponseAsync (
538569 subPlugin . DataSource ,
539- responseLines ,
570+ responseBuilder ,
540571 cancellationToken
541572 ) . ConfigureAwait ( false ) ;
542573 }
543574 }
544575 else {
545576 await WriteFetchResponseAsync (
546577 plugin . DataSource ,
547- responseLines ,
578+ responseBuilder ,
548579 cancellationToken
549580 ) . ConfigureAwait ( false ) ;
550581 }
551582
552- responseLines . Add ( "." ) ;
583+ responseBuilder . Append ( ".\n " ) ;
553584
554585 await SendResponseAsync (
555586 client : client ,
556- responseLines : responseLines ,
587+ responseBuilder : responseBuilder ,
557588 cancellationToken : cancellationToken
558589 ) . ConfigureAwait ( false ) ;
559590 }
560591 finally {
561- responseLineListPool . Return ( responseLines ) ;
592+ responseBuilderPool . Return ( responseBuilder ) ;
562593 }
563594 }
564595
565596 private static async ValueTask WriteFetchResponseAsync (
566597 IPluginDataSource dataSource ,
567- List < string > responseLines ,
598+ StringBuilder responseBuilder ,
568599 CancellationToken cancellationToken
569600 )
570601 {
@@ -573,7 +604,7 @@ CancellationToken cancellationToken
573604 cancellationToken : cancellationToken
574605 ) . ConfigureAwait ( false ) ;
575606
576- responseLines . Add ( $ "{ field . Name } .value { valueString } ") ;
607+ responseBuilder . Append ( provider : null , $ "{ field . Name } .value { valueString } \n ") ;
577608 }
578609 }
579610
@@ -606,18 +637,18 @@ CancellationToken cancellationToken
606637
607638 async ValueTask HandleConfigCommandAsyncCore ( )
608639 {
609- var responseLines = responseLineListPool . Take ( ) ;
640+ var responseBuilder = responseBuilderPool . Take ( ) ;
610641
611642 try {
612643 if ( plugin is IMultigraphPlugin multigraphPlugin ) {
613644 // 'Protocol extension: multiple graphs from one plugin' (https://guide.munin-monitoring.org/en/latest/plugin/protocol-multigraph.html)
614645 foreach ( var subPlugin in multigraphPlugin . Plugins ) {
615- responseLines . Add ( $ "multigraph { subPlugin . Name } ") ;
646+ responseBuilder . Append ( provider : null , $ "multigraph { subPlugin . Name } \n ") ;
616647
617648 await WriteConfigResponseAsync (
618649 subPlugin ,
619650 includeFetchResponse : IsDirtyConfigEnabled ,
620- responseLines ,
651+ responseBuilder ,
621652 cancellationToken
622653 ) . ConfigureAwait ( false ) ;
623654 }
@@ -626,40 +657,40 @@ await WriteConfigResponseAsync(
626657 await WriteConfigResponseAsync (
627658 plugin ,
628659 includeFetchResponse : IsDirtyConfigEnabled ,
629- responseLines ,
660+ responseBuilder ,
630661 cancellationToken
631662 ) . ConfigureAwait ( false ) ;
632663 }
633664
634- responseLines . Add ( "." ) ;
665+ responseBuilder . Append ( ".\n " ) ;
635666
636667 await SendResponseAsync (
637668 client : client ,
638- responseLines : responseLines ,
669+ responseBuilder : responseBuilder ,
639670 cancellationToken : cancellationToken
640671 ) . ConfigureAwait ( false ) ;
641672 }
642673 finally {
643- responseLineListPool . Return ( responseLines ) ;
674+ responseBuilderPool . Return ( responseBuilder ) ;
644675 }
645676 }
646677
647678 static ValueTask WriteConfigResponseAsync (
648679 IPlugin plugin ,
649680 bool includeFetchResponse ,
650- List < string > responseLines ,
681+ StringBuilder responseBuilder ,
651682 CancellationToken cancellationToken
652683 )
653684 {
654685 WriteConfigResponse (
655686 plugin ,
656- responseLines
687+ responseBuilder
657688 ) ;
658689
659690 if ( includeFetchResponse ) {
660691 return WriteFetchResponseAsync (
661692 dataSource : plugin . DataSource ,
662- responseLines : responseLines ,
693+ responseBuilder : responseBuilder ,
663694 cancellationToken : cancellationToken
664695 ) ;
665696 }
@@ -670,28 +701,28 @@ CancellationToken cancellationToken
670701
671702 private static void WriteConfigResponse (
672703 IPlugin plugin ,
673- List < string > responseLines
704+ StringBuilder responseBuilder
674705 )
675706 {
676707 /*
677708 * global attributes
678709 */
679- responseLines . AddRange (
680- plugin . GraphAttributes . EnumerateAttributes ( )
681- ) ;
710+ foreach ( var graphAttribute in plugin . GraphAttributes . EnumerateAttributes ( ) ) {
711+ responseBuilder . Append ( graphAttribute ) . Append ( ' \n ' ) ;
712+ }
682713
683714 /*
684715 * data source attributes
685716 */
686717 WriteConfigDataSourceAttributes (
687718 plugin . DataSource ,
688- responseLines
719+ responseBuilder
689720 ) ;
690721 }
691722
692723 private static void WriteConfigDataSourceAttributes (
693724 IPluginDataSource dataSource ,
694- List < string > responseLines
725+ StringBuilder responseBuilder
695726 )
696727 {
697728 var shouldHandleNegativeFields = dataSource
@@ -710,32 +741,32 @@ List<string> responseLines
710741 var fieldAttrs = field . Attributes ;
711742 bool ? graph = null ;
712743
713- responseLines . Add ( $ "{ field . Name } .label { fieldAttrs . Label } ") ;
744+ responseBuilder . Append ( provider : null , $ "{ field . Name } .label { fieldAttrs . Label } \n ") ;
714745
715746 if ( TranslateFieldDrawAttribute ( fieldAttrs . GraphStyle ) is string attrDraw )
716- responseLines . Add ( $ "{ field . Name } .draw { attrDraw } ") ;
747+ responseBuilder . Append ( provider : null , $ "{ field . Name } .draw { attrDraw } \n ") ;
717748
718749 if ( FormatNormalValueRange ( fieldAttrs . NormalRangeForWarning ) is string attrWarning )
719- responseLines . Add ( $ "{ field . Name } .warning { attrWarning } ") ;
750+ responseBuilder . Append ( provider : null , $ "{ field . Name } .warning { attrWarning } \n ") ;
720751
721752 if ( FormatNormalValueRange ( fieldAttrs . NormalRangeForCritical ) is string attrCritical )
722- responseLines . Add ( $ "{ field . Name } .critical { attrCritical } ") ;
753+ responseBuilder . Append ( provider : null , $ "{ field . Name } .critical { attrCritical } \n ") ;
723754
724755 if ( shouldHandleNegativeFields && ! string . IsNullOrEmpty ( fieldAttrs . NegativeFieldName ) ) {
725756 var negativeField = dataSource . Fields . FirstOrDefault (
726757 f => string . Equals ( fieldAttrs . NegativeFieldName , f . Name , StringComparison . Ordinal )
727758 ) ;
728759
729760 if ( negativeField is not null )
730- responseLines . Add ( $ "{ field . Name } .negative { negativeField . Name } ") ;
761+ responseBuilder . Append ( provider : null , $ "{ field . Name } .negative { negativeField . Name } \n ") ;
731762 }
732763
733764 // this field is defined as the negative field of other field, so should not be graphed
734765 if ( shouldHandleNegativeFields && IsNegativeField ( field , dataSource . Fields ) )
735766 graph = false ;
736767
737768 if ( graph is bool drawGraph )
738- responseLines . Add ( $ "{ field . Name } .graph { ( drawGraph ? "yes" : "no" ) } ") ;
769+ responseBuilder . Append ( provider : null , $ "{ field . Name } .graph { ( drawGraph ? "yes" : "no" ) } \n ") ;
739770 }
740771
741772 static bool IsNegativeField ( IPluginField field , IReadOnlyCollection < IPluginField > fields )
0 commit comments