From 3bc294c6fc9f3a182dd4c0565555cfcb5af16d87 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Tue, 28 May 2024 19:12:43 -0700 Subject: [PATCH 1/4] Improve TextSpan and For statements --- Fluid/Ast/ForStatement.cs | 6 +- Fluid/Ast/TextSpanStatement.cs | 182 +++++++++++++++++---------------- Fluid/Values/FluidValues.cs | 1 + Fluid/Values/FunctionValue.cs | 2 +- 4 files changed, 101 insertions(+), 90 deletions(-) diff --git a/Fluid/Ast/ForStatement.cs b/Fluid/Ast/ForStatement.cs index c701528f..dc470915 100644 --- a/Fluid/Ast/ForStatement.cs +++ b/Fluid/Ast/ForStatement.cs @@ -5,7 +5,6 @@ namespace Fluid.Ast { public sealed class ForStatement : TagStatement { - private readonly bool _isContinueOffset; private readonly string _continueOffsetLiteral; public ForStatement( @@ -25,7 +24,7 @@ public ForStatement( Reversed = reversed; Else = elseStatement; - _isContinueOffset = Offset is MemberExpression l && l.Segments.Count == 1 && ((IdentifierSegment)l.Segments[0]).Identifier == "continue"; + OffSetIsContinue = Offset is MemberExpression l && l.Segments.Count == 1 && ((IdentifierSegment)l.Segments[0]).Identifier == "continue"; _continueOffsetLiteral = source is MemberExpression m ? "for_continue_" + ((IdentifierSegment)m.Segments[0]).Identifier : null; } @@ -36,6 +35,7 @@ public ForStatement( public Expression Offset { get; } public bool Reversed { get; } public ElseStatement Else { get; } + public bool OffSetIsContinue { get; } public override async ValueTask WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) { @@ -55,7 +55,7 @@ public override async ValueTask WriteToAsync(TextWriter writer, Text var startIndex = 0; if (Offset is not null) { - if (_isContinueOffset) + if (OffSetIsContinue) { startIndex = (int)context.GetValue(_continueOffsetLiteral).ToNumberValue(); } diff --git a/Fluid/Ast/TextSpanStatement.cs b/Fluid/Ast/TextSpanStatement.cs index 11917733..a3766d05 100644 --- a/Fluid/Ast/TextSpanStatement.cs +++ b/Fluid/Ast/TextSpanStatement.cs @@ -1,16 +1,15 @@ -using Parlot; +using Fluid.Utils; +using Parlot; using System.Text.Encodings.Web; -using Fluid.Utils; namespace Fluid.Ast { public sealed class TextSpanStatement : Statement { - private bool _isStripped; - private bool _isEmpty; + private bool _isBufferPrepared; private readonly object _synLock = new(); private TextSpan _text; - private string _buffer; + internal string _preparedBuffer; public TextSpanStatement(in TextSpan text) { @@ -32,115 +31,128 @@ public TextSpanStatement(string text) public ref readonly TextSpan Text => ref _text; - public override ValueTask WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) + public string Buffer => _preparedBuffer; + + public void PrepareBuffer(TemplateOptions options) { - if (!_isStripped) + if (_isBufferPrepared) + { + return; + } + + // Prevent two threads from stripping the same statement in case WriteToAsync is called concurrently + lock (_synLock) { - // Prevent two threads from strsipping the same statement in case WriteToAsync is called concurrently - // - lock (_synLock) + if (!_isBufferPrepared) { - if (!_isStripped) + var trimming = options.Trimming; + StripLeft |= + (PreviousIsTag && (trimming & TrimmingFlags.TagRight) != 0) || + (PreviousIsOutput && (trimming & TrimmingFlags.OutputRight) != 0) + ; + + StripRight |= + (NextIsTag && (trimming & TrimmingFlags.TagLeft) != 0) || + (NextIsOutput && (trimming & TrimmingFlags.OutputLeft) != 0) + ; + + var span = _text.Buffer; + var start = 0; + var end = _text.Length - 1; + + // Does this text need to have its left part trimmed? + if (StripLeft) { - var trimming = context.Options.Trimming; - StripLeft |= - (PreviousIsTag && (trimming & TrimmingFlags.TagRight) != 0) || - (PreviousIsOutput && (trimming & TrimmingFlags.OutputRight) != 0) - ; - - StripRight |= - (NextIsTag && (trimming & TrimmingFlags.TagLeft) != 0) || - (NextIsOutput && (trimming & TrimmingFlags.OutputLeft) != 0) - ; - - var span = _text.Buffer; - var start = 0; - var end = _text.Length - 1; - - // Does this text need to have its left part trimmed? - if (StripLeft) + var firstNewLine = -1; + + for (var i = start; i <= end; i++) { - var firstNewLine = -1; + var c = span[_text.Offset + i]; - for (var i = start; i <= end; i++) + if (Character.IsWhiteSpaceOrNewLine(c)) { - var c = span[_text.Offset + i]; + start++; - if (Character.IsWhiteSpaceOrNewLine(c)) + if (firstNewLine == -1 && (c == '\n')) { - start++; - - if (firstNewLine == -1 && (c == '\n')) - { - firstNewLine = start; - } - } - else - { - break; + firstNewLine = start; } } - - if (!context.Options.Greedy) + else { - if (firstNewLine != -1) - { - start = firstNewLine; - } + break; } } - // Does this text need to have its right part trimmed? - if (StripRight) + if (!options.Greedy) { - var lastNewLine = -1; - - for (var i = end; i >= start; i--) + if (firstNewLine != -1) { - var c = span[_text.Offset + i]; + start = firstNewLine; + } + } + } - if (Character.IsWhiteSpaceOrNewLine(c)) - { - if (lastNewLine == -1 && c == '\n') - { - lastNewLine = end; - } + // Does this text need to have its right part trimmed? + if (StripRight) + { + var lastNewLine = -1; - end--; - } - else - { - break; - } - } + for (var i = end; i >= start; i--) + { + var c = span[_text.Offset + i]; - if (!context.Options.Greedy) + if (Character.IsWhiteSpaceOrNewLine(c)) { - if (lastNewLine != -1) + if (lastNewLine == -1 && c == '\n') { - end = lastNewLine; + lastNewLine = end; } + + end--; + } + else + { + break; } } - if (end - start + 1 == 0) - { - _isEmpty = true; - } - else if (start != 0 || end != _text.Length - 1) - { - var offset = _text.Offset; - var buffer = _text.Buffer; - _text = new TextSpan(buffer, offset + start, end - start + 1); + if (!options.Greedy) + { + if (lastNewLine != -1) + { + end = lastNewLine; + } } + } + if (end - start + 1 == 0) + { + _text = ""; + } + else if (start != 0 || end != _text.Length - 1) + { + var offset = _text.Offset; + var buffer = _text.Buffer; - _buffer = _text.ToString(); - _isStripped = true; + _text = new TextSpan(buffer, offset + start, end - start + 1); } + + _preparedBuffer = _text.ToString(); + _isBufferPrepared = true; } } + } + + protected internal override Statement Accept(AstVisitor visitor) => visitor.VisitTextSpanStatement(this); - if (_isEmpty) + public override ValueTask WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) + { + if (!_isBufferPrepared) + { + PrepareBuffer(context.Options); + } + + if (_preparedBuffer == "") { return new ValueTask(Completion.Normal); } @@ -157,7 +169,7 @@ static async ValueTask Awaited(Task task) return Completion.Normal; } - var task = writer.WriteAsync(_buffer); + var task = writer.WriteAsync(_preparedBuffer); if (!task.IsCompletedSuccessfully()) { return Awaited(task); @@ -165,7 +177,5 @@ static async ValueTask Awaited(Task task) return new ValueTask(Completion.Normal); } - - protected internal override Statement Accept(AstVisitor visitor) => visitor.VisitTextSpanStatement(this); } -} +} \ No newline at end of file diff --git a/Fluid/Values/FluidValues.cs b/Fluid/Values/FluidValues.cs index 30ca0099..950ac0bb 100644 --- a/Fluid/Values/FluidValues.cs +++ b/Fluid/Values/FluidValues.cs @@ -12,5 +12,6 @@ public enum FluidValues Object = 7, String = 8, DateTime = 9, + Function = 10, } } diff --git a/Fluid/Values/FunctionValue.cs b/Fluid/Values/FunctionValue.cs index d43dd207..27fc2429 100644 --- a/Fluid/Values/FunctionValue.cs +++ b/Fluid/Values/FunctionValue.cs @@ -18,7 +18,7 @@ public FunctionValue(Func action _action = (args, c) => new ValueTask(action(args, c)); } - public override FluidValues Type => FluidValues.Object; + public override FluidValues Type => FluidValues.Function; public override ValueTask InvokeAsync(FunctionArguments arguments, TemplateContext context) { From ee382bb72c09dcabdebd457bec451be2790e93a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Ros?= Date: Wed, 29 May 2024 14:07:15 -0700 Subject: [PATCH 2/4] Update Fluid/Ast/ForStatement.cs Co-authored-by: Hisham Bin Ateya --- Fluid/Ast/ForStatement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Fluid/Ast/ForStatement.cs b/Fluid/Ast/ForStatement.cs index dc470915..f88b2703 100644 --- a/Fluid/Ast/ForStatement.cs +++ b/Fluid/Ast/ForStatement.cs @@ -35,7 +35,7 @@ public ForStatement( public Expression Offset { get; } public bool Reversed { get; } public ElseStatement Else { get; } - public bool OffSetIsContinue { get; } + public bool OffsetIsContinue { get; } public override async ValueTask WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) { From 2a9ba0f0af8ed0c95fc58ea085d78795f75717c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Ros?= Date: Wed, 29 May 2024 14:07:19 -0700 Subject: [PATCH 3/4] Update Fluid/Ast/ForStatement.cs Co-authored-by: Hisham Bin Ateya --- Fluid/Ast/ForStatement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Fluid/Ast/ForStatement.cs b/Fluid/Ast/ForStatement.cs index f88b2703..8b3f4a83 100644 --- a/Fluid/Ast/ForStatement.cs +++ b/Fluid/Ast/ForStatement.cs @@ -24,7 +24,7 @@ public ForStatement( Reversed = reversed; Else = elseStatement; - OffSetIsContinue = Offset is MemberExpression l && l.Segments.Count == 1 && ((IdentifierSegment)l.Segments[0]).Identifier == "continue"; + OffsetIsContinue = Offset is MemberExpression l && l.Segments.Count == 1 && ((IdentifierSegment)l.Segments[0]).Identifier == "continue"; _continueOffsetLiteral = source is MemberExpression m ? "for_continue_" + ((IdentifierSegment)m.Segments[0]).Identifier : null; } From 548ce2056aaca0687a3d158875149443c552e3ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Ros?= Date: Wed, 29 May 2024 14:07:23 -0700 Subject: [PATCH 4/4] Update Fluid/Ast/ForStatement.cs Co-authored-by: Hisham Bin Ateya --- Fluid/Ast/ForStatement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Fluid/Ast/ForStatement.cs b/Fluid/Ast/ForStatement.cs index 8b3f4a83..5cbb83dd 100644 --- a/Fluid/Ast/ForStatement.cs +++ b/Fluid/Ast/ForStatement.cs @@ -55,7 +55,7 @@ public override async ValueTask WriteToAsync(TextWriter writer, Text var startIndex = 0; if (Offset is not null) { - if (OffSetIsContinue) + if (OffsetIsContinue) { startIndex = (int)context.GetValue(_continueOffsetLiteral).ToNumberValue(); }