diff --git a/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/Slice.feature b/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/Slice.feature new file mode 100644 index 0000000..50b05e2 --- /dev/null +++ b/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/Slice.feature @@ -0,0 +1,69 @@ +Feature: Slice values from a ValueStringBuilder + +Scenario Outline: Slice the ValueStringBuilder + Given a ValueStringBuilder initialized with '' of length + And I append 'Hello' to the ValueStringBuilder + And I slice characters from the start of the ValueStringBuilder + When I get the Span from the ValueStringBuilder + Then the ValueStringBuilder string should be '' + Examples: + | InitializationType | InitialLength | Start | ExpectedValue | + | Span | 10 | 0 | Hello | + | Capacity | 10 | 0 | Hello | + | Span | 10 | 1 | ello | + | Capacity | 10 | 1 | ello | + | Span | 10 | 2 | llo | + | Capacity | 10 | 2 | llo | + | Span | 10 | 5 | | + | Capacity | 10 | 5 | | + + Scenario Outline: Slice the ValueStringBuilder with start and length + Given a ValueStringBuilder initialized with '' of length + And I append 'Hello' to the ValueStringBuilder + And I slice characters from the start of the ValueStringBuilder with length + When I get the Span from the ValueStringBuilder + Then the ValueStringBuilder string should be '' + Examples: + | InitializationType | InitialLength | Start | Length | ExpectedValue | + | Span | 10 | 0 | 5 | Hello | + | Capacity | 10 | 0 | 5 | Hello | + | Span | 10 | 1 | 4 | ello | + | Capacity | 10 | 1 | 4 | ello | + | Span | 10 | 2 | 3 | llo | + | Capacity | 10 | 2 | 3 | llo | + | Span | 10 | 0 | 4 | Hell | + | Capacity | 10 | 0 | 4 | Hell | + | Span | 10 | 1 | 3 | ell | + | Capacity | 10 | 1 | 3 | ell | + | Span | 10 | 2 | 2 | ll | + | Capacity | 10 | 2 | 2 | ll | + | Span | 10 | 1 | 0 | | + | Capacity | 10 | 1 | 0 | | + | Span | 10 | 0 | 0 | | + | Capacity | 10 | 0 | 0 | | + +Scenario Outline: Specified range is out of bounds (start and length) + Given a ValueStringBuilder initialized with '' of length 25 + And I append 'Hello' to the ValueStringBuilder + And I slice characters from the start of the ValueStringBuilder with length + Then the attempt should have thrown a 'System.ArgumentOutOfRangeException' + Examples: + | InitializationType | Start | Length | + | Span | 0 | 6 | + | Span | 1 | 5 | + | Span | 5 | 1 | + | Span | 6 | 0 | + | Capacity | 0 | 6 | + | Capacity | 1 | 5 | + | Capacity | 5 | 1 | + | Capacity | 6 | 0 | + + Scenario Outline: Specified range is out of bounds (start only) + Given a ValueStringBuilder initialized with '' of length 25 + And I append 'Hello' to the ValueStringBuilder + And I slice characters from the start of the ValueStringBuilder + Then the attempt should have thrown a 'System.ArgumentOutOfRangeException' + Examples: + | InitializationType | Start | + | Span | 6 | + | Capacity | 7 | diff --git a/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/Utf8/SliceUtf8.feature b/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/Utf8/SliceUtf8.feature new file mode 100644 index 0000000..b6ffb66 --- /dev/null +++ b/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/Utf8/SliceUtf8.feature @@ -0,0 +1,69 @@ +Feature: Slice values from a UTF-8 ValueStringBuilder + +Scenario Outline: Slice the UTF-8 ValueStringBuilder + Given a UTF-8 ValueStringBuilder initialized with '' of length + And I append 'Hello' to the UTF-8 ValueStringBuilder + And I slice characters from the start of the UTF-8 ValueStringBuilder + When I get the Span from the UTF-8 ValueStringBuilder + Then the UTF-8 ValueStringBuilder string should be '' + Examples: + | InitializationType | InitialLength | Start | ExpectedValue | + | Span | 10 | 0 | Hello | + | Capacity | 10 | 0 | Hello | + | Span | 10 | 1 | ello | + | Capacity | 10 | 1 | ello | + | Span | 10 | 2 | llo | + | Capacity | 10 | 2 | llo | + | Span | 10 | 5 | | + | Capacity | 10 | 5 | | + + Scenario Outline: Slice the UTF-8 ValueStringBuilder with start and length + Given a UTF-8 ValueStringBuilder initialized with '' of length + And I append 'Hello' to the UTF-8 ValueStringBuilder + And I slice characters from the start of the UTF-8 ValueStringBuilder with length + When I get the Span from the UTF-8 ValueStringBuilder + Then the UTF-8 ValueStringBuilder string should be '' + Examples: + | InitializationType | InitialLength | Start | Length | ExpectedValue | + | Span | 10 | 0 | 5 | Hello | + | Capacity | 10 | 0 | 5 | Hello | + | Span | 10 | 1 | 4 | ello | + | Capacity | 10 | 1 | 4 | ello | + | Span | 10 | 2 | 3 | llo | + | Capacity | 10 | 2 | 3 | llo | + | Span | 10 | 0 | 4 | Hell | + | Capacity | 10 | 0 | 4 | Hell | + | Span | 10 | 1 | 3 | ell | + | Capacity | 10 | 1 | 3 | ell | + | Span | 10 | 2 | 2 | ll | + | Capacity | 10 | 2 | 2 | ll | + | Span | 10 | 1 | 0 | | + | Capacity | 10 | 1 | 0 | | + | Span | 10 | 0 | 0 | | + | Capacity | 10 | 0 | 0 | | + +Scenario Outline: Specified range is out of bounds (start and length) + Given a UTF-8 ValueStringBuilder initialized with '' of length 25 + And I append 'Hello' to the UTF-8 ValueStringBuilder + And I slice characters from the start of the UTF-8 ValueStringBuilder with length + Then the attempt should have thrown a 'System.ArgumentOutOfRangeException' + Examples: + | InitializationType | Start | Length | + | Span | 0 | 6 | + | Span | 1 | 5 | + | Span | 5 | 1 | + | Span | 6 | 0 | + | Capacity | 0 | 6 | + | Capacity | 1 | 5 | + | Capacity | 5 | 1 | + | Capacity | 6 | 0 | + + Scenario Outline: Specified range is out of bounds (start only) + Given a UTF-8 ValueStringBuilder initialized with '' of length 25 + And I append 'Hello' to the UTF-8 ValueStringBuilder + And I slice characters from the start of the UTF-8 ValueStringBuilder + Then the attempt should have thrown a 'System.ArgumentOutOfRangeException' + Examples: + | InitializationType | Start | + | Span | 6 | + | Capacity | 7 | diff --git a/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/Utf8/Utf8ValueStringBuilderStepDefinitions.cs b/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/Utf8/Utf8ValueStringBuilderStepDefinitions.cs index ef91683..a2e5cc9 100644 --- a/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/Utf8/Utf8ValueStringBuilderStepDefinitions.cs +++ b/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/Utf8/Utf8ValueStringBuilderStepDefinitions.cs @@ -53,6 +53,34 @@ public void GivenIAttemptToReplaceWithAtIndexWithCount( this.Driver.Execute(); } + [Given("I slice {int} characters from the start of the UTF-8 ValueStringBuilder")] + public void GivenISliceCharactersFromTheStartOfTheUtf8ValueStringBuilder( + int start) + { + this.Driver.AddOperation(new Utf8ValueStringBuilderTestDriver.AttemptSliceOperation(start, exceptionSteps)); + try + { + this.Driver.Execute(); + } + catch (ArgumentOutOfRangeException) + { + } + } + + [Given("I slice {int} characters from the start of the UTF-8 ValueStringBuilder with length {int}")] + public void GivenISliceCharactersFromTheStartOfTheUtf8ValueStringBuilderWithLength( + int start, int length) + { + this.Driver.AddOperation(new Utf8ValueStringBuilderTestDriver.AttemptSliceWithLengthOperation(start, length, exceptionSteps)); + try + { + this.Driver.Execute(); + } + catch (ArgumentOutOfRangeException) + { + } + } + [When("I get the string from the UTF-8 ValueStringBuilder via {string}")] public void WhenIGetTheStringFromTheUtf8ValueStringBuilderVia(Utf8ValueStringBuilderValueFrom mechanism) { diff --git a/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/Utf8/Utf8ValueStringBuilderTestDriver.cs b/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/Utf8/Utf8ValueStringBuilderTestDriver.cs index 25367f6..d5e6c58 100644 --- a/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/Utf8/Utf8ValueStringBuilderTestDriver.cs +++ b/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/Utf8/Utf8ValueStringBuilderTestDriver.cs @@ -160,4 +160,41 @@ public override void Execute(ref ValueStringBuilder sb) } } } + + public class AttemptSliceOperation( + int count, + ExceptionStepDefinitions exceptionSteps) + : OperationBase + { + public override void Execute(ref ValueStringBuilder sb) + { + try + { + sb.ApplySlice(count); + } + catch (Exception ex) + { + exceptionSteps.Exception = ex; + } + } + } + + public class AttemptSliceWithLengthOperation( + int count, + int length, + ExceptionStepDefinitions exceptionSteps) + : OperationBase + { + public override void Execute(ref ValueStringBuilder sb) + { + try + { + sb.ApplySlice(count, length); + } + catch (Exception ex) + { + exceptionSteps.Exception = ex; + } + } + } } \ No newline at end of file diff --git a/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/ValueStringBuilderStepDefinitions.cs b/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/ValueStringBuilderStepDefinitions.cs index 362a5f4..1a4fcec 100644 --- a/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/ValueStringBuilderStepDefinitions.cs +++ b/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/ValueStringBuilderStepDefinitions.cs @@ -7,6 +7,7 @@ using Corvus.HighPerformance.Specs; using Reqnroll; +using ValueStringBuilderFeatures.Utf8; namespace ValueStringBuilderFeatures; @@ -65,6 +66,34 @@ public void GivenIAttemptToReplaceWithAtIndexWithCount( this.Driver.Execute(); } + [Given("I slice {int} characters from the start of the ValueStringBuilder")] + public void GivenISliceCharactersFromTheStartOfTheUtf8ValueStringBuilder( + int start) + { + this.Driver.AddOperation(new ValueStringBuilderTestDriver.AttemptSliceOperation(start, exceptionSteps)); + try + { + this.Driver.Execute(); + } + catch (ArgumentOutOfRangeException) + { + } + } + + [Given("I slice {int} characters from the start of the ValueStringBuilder with length {int}")] + public void GivenISliceCharactersFromTheStartOfTheUtf8ValueStringBuilderWithLength( + int start, int length) + { + this.Driver.AddOperation(new ValueStringBuilderTestDriver.AttemptSliceWithLengthOperation(start, length, exceptionSteps)); + try + { + this.Driver.Execute(); + } + catch (ArgumentOutOfRangeException) + { + } + } + [When("I get the string from the ValueStringBuilder via {string}")] public void WhenIGetTheStringFromTheValueStringBuilderVia(ValueStringBuilderValueFrom mechanism) { diff --git a/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/ValueStringBuilderTestDriver.cs b/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/ValueStringBuilderTestDriver.cs index d843abd..e25b64d 100644 --- a/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/ValueStringBuilderTestDriver.cs +++ b/Solutions/Corvus.HighPerformance.Specs/ValueStringBuilderFeatures/ValueStringBuilderTestDriver.cs @@ -170,4 +170,41 @@ public override void Execute(ref ValueStringBuilder sb) } } } + + public class AttemptSliceOperation( + int count, + ExceptionStepDefinitions exceptionSteps) + : OperationBase + { + public override void Execute(ref ValueStringBuilder sb) + { + try + { + sb.ApplySlice(count); + } + catch (Exception ex) + { + exceptionSteps.Exception = ex; + } + } + } + + public class AttemptSliceWithLengthOperation( + int count, + int length, + ExceptionStepDefinitions exceptionSteps) + : OperationBase + { + public override void Execute(ref ValueStringBuilder sb) + { + try + { + sb.ApplySlice(count, length); + } + catch (Exception ex) + { + exceptionSteps.Exception = ex; + } + } + } } \ No newline at end of file diff --git a/Solutions/Corvus.HighPerformance.Specs/packages.lock.json b/Solutions/Corvus.HighPerformance.Specs/packages.lock.json index a9d2df7..e0defb9 100644 --- a/Solutions/Corvus.HighPerformance.Specs/packages.lock.json +++ b/Solutions/Corvus.HighPerformance.Specs/packages.lock.json @@ -27,6 +27,15 @@ "Microsoft.CodeCoverage": "17.11.1" } }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net481": "1.0.3" + } + }, "MSTest.TestAdapter": { "type": "Direct", "requested": "[3.6.0, )", @@ -54,21 +63,6 @@ "Reqnroll.Tools.MsBuild.Generation": "[2.1.0]" } }, - "Roslynator.Analyzers": { - "type": "Direct", - "requested": "[4.12.3, )", - "resolved": "4.12.3", - "contentHash": "6+O9GwAxOxJr1rZ4aaLiBiSH3pxDkJuXLE8qjo/JXLvGkZhKRLbAz30WA32RmJb5Nn1TmjZ2T3kWt3GJmEDj1w==" - }, - "StyleCop.Analyzers": { - "type": "Direct", - "requested": "[1.2.0-beta.556, )", - "resolved": "1.2.0-beta.556", - "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==", - "dependencies": { - "StyleCop.Analyzers.Unstable": "1.2.0.556" - } - }, "Cucumber.CucumberExpressions": { "type": "Transitive", "resolved": "17.1.0", @@ -129,6 +123,11 @@ "System.Text.Json": "8.0.4" } }, + "Microsoft.NETFramework.ReferenceAssemblies.net481": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "Vv/20vgHS7VglVOVh8J3Iz/MA+VYKVRp9f7r2qiKBMuzviTOmocG70yq0Q8T5OTmCONkEAIJwETD1zhEfLkAXQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "1.1.1", @@ -218,11 +217,6 @@ "resolved": "1.0.8", "contentHash": "lVCC/Rie7N5rFoc7YxPS0nneLfsWSTIMMlkndwxhaD8MxBp3Bsv1HeiVjVwXCjWaQeoqZcvIy52fF5Xit00ZLw==" }, - "StyleCop.Analyzers.Unstable": { - "type": "Transitive", - "resolved": "1.2.0.556", - "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ==" - }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", @@ -375,21 +369,6 @@ "Reqnroll.Tools.MsBuild.Generation": "[2.1.0]" } }, - "Roslynator.Analyzers": { - "type": "Direct", - "requested": "[4.12.3, )", - "resolved": "4.12.3", - "contentHash": "6+O9GwAxOxJr1rZ4aaLiBiSH3pxDkJuXLE8qjo/JXLvGkZhKRLbAz30WA32RmJb5Nn1TmjZ2T3kWt3GJmEDj1w==" - }, - "StyleCop.Analyzers": { - "type": "Direct", - "requested": "[1.2.0-beta.556, )", - "resolved": "1.2.0-beta.556", - "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==", - "dependencies": { - "StyleCop.Analyzers.Unstable": "1.2.0.556" - } - }, "Cucumber.CucumberExpressions": { "type": "Transitive", "resolved": "17.1.0", @@ -548,11 +527,6 @@ "resolved": "1.0.8", "contentHash": "lVCC/Rie7N5rFoc7YxPS0nneLfsWSTIMMlkndwxhaD8MxBp3Bsv1HeiVjVwXCjWaQeoqZcvIy52fF5Xit00ZLw==" }, - "StyleCop.Analyzers.Unstable": { - "type": "Transitive", - "resolved": "1.2.0.556", - "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ==" - }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", diff --git a/Solutions/Corvus.HighPerformance/Corvus.HighPerformance/Utf8/ValueStringBuilder.cs b/Solutions/Corvus.HighPerformance/Corvus.HighPerformance/Utf8/ValueStringBuilder.cs index 516bbb6..7ba8e6e 100644 --- a/Solutions/Corvus.HighPerformance/Corvus.HighPerformance/Utf8/ValueStringBuilder.cs +++ b/Solutions/Corvus.HighPerformance/Corvus.HighPerformance/Utf8/ValueStringBuilder.cs @@ -153,6 +153,37 @@ public ReadOnlyMemory CreateMemoryAndDispose() /// Returns the underlying storage of the builder. public Span RawBytes => _bytes; + + /// + /// Slice the builder to remove the first bytes. + /// + /// The number of bytes to slice from the start. + public void ApplySlice(int start) + { + if (start > _pos) + { + throw new ArgumentOutOfRangeException(); + } + + _bytes = _bytes.Slice(start); + _pos -= start; + } + + /// + /// Slice the builder to the specified range. + /// + /// The number of bytes to slice from the start. + /// The final length of the span. + public void ApplySlice(int start, int length) + { + if(start + length > _pos) + { + throw new ArgumentOutOfRangeException(); + } + + _bytes = _bytes.Slice(start); + _pos = length; + } /// /// Returns a span around the contents of the builder. diff --git a/Solutions/Corvus.HighPerformance/Corvus.HighPerformance/ValueStringBuilder.cs b/Solutions/Corvus.HighPerformance/Corvus.HighPerformance/ValueStringBuilder.cs index f44d1be..e7391a0 100644 --- a/Solutions/Corvus.HighPerformance/Corvus.HighPerformance/ValueStringBuilder.cs +++ b/Solutions/Corvus.HighPerformance/Corvus.HighPerformance/ValueStringBuilder.cs @@ -152,6 +152,37 @@ public string CreateStringAndDispose() /// Returns the underlying storage of the builder. public Span RawChars => _chars; + /// + /// Slice the builder to remove the first bytes. + /// + /// The number of bytes to slice from the start. + public void ApplySlice(int start) + { + if (start > _pos) + { + throw new ArgumentOutOfRangeException(); + } + + _chars = _chars.Slice(start); + _pos -= start; + } + + /// + /// Slice the builder to the specified range. + /// + /// The number of bytes to slice from the start. + /// The final length of the span. + public void ApplySlice(int start, int length) + { + if (start + length > _pos) + { + throw new ArgumentOutOfRangeException(); + } + + _chars = _chars.Slice(start); + _pos = length; + } + /// /// Returns a span around the contents of the builder. ///