Merge remote-tracking branch 'origin/v10/dev' into v11/dev
# Conflicts: # src/Umbraco.Core/Routing/UmbracoRequestPaths.cs
This commit is contained in:
@@ -2660,6 +2660,7 @@ Per gestire il tuo sito web, è sufficiente aprire il backoffice di Umbraco e in
|
||||
<key alias="labelUsedByMemberTypes">Usato nei tipi di membro</key>
|
||||
<key alias="noMemberTypes">Non ci sono riferimenti a tipi di membro.</key>
|
||||
<key alias="usedByProperties">Usato da</key>
|
||||
<key alias="labelUsedByItems">Correlato ai seguenti elementi</key>
|
||||
<key alias="labelUsedByDocuments">Usato nei documenti</key>
|
||||
<key alias="labelUsedByMembers">Usato nei membri</key>
|
||||
<key alias="labelUsedByMedia">Usato nei media</key>
|
||||
|
||||
@@ -1040,14 +1040,15 @@ public static class StringExtensions
|
||||
throw new ArgumentNullException(nameof(text));
|
||||
}
|
||||
|
||||
var pos = text.IndexOf(search, StringComparison.InvariantCulture);
|
||||
ReadOnlySpan<char> spanText = text.AsSpan();
|
||||
var pos = spanText.IndexOf(search, StringComparison.InvariantCulture);
|
||||
|
||||
if (pos < 0)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
|
||||
return string.Concat(spanText[..pos], replace.AsSpan(), spanText[(pos + search.Length)..]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models;
|
||||
@@ -132,7 +133,7 @@ public class PropertyGroup : EntityBase, IEquatable<PropertyGroup>
|
||||
}
|
||||
|
||||
public bool Equals(PropertyGroup? other) =>
|
||||
base.Equals(other) || (other != null && Type == other.Type && Alias == other.Alias);
|
||||
base.Equals(other) || (other != null && Type == other.Type && Alias == other.Alias && Id == other.Id);
|
||||
|
||||
public override int GetHashCode() => (base.GetHashCode(), Type, Alias).GetHashCode();
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ public class PropertyTypeCollection : KeyedCollection<string, IPropertyType>, IN
|
||||
// This baseclass calling is needed, else compiler will complain about nullability
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsReadOnly => ((ICollection<IPropertyType>)this).IsReadOnly;
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
// 'new' keyword is required! we can explicitly implement ICollection<IPropertyType>.Add BUT since normally a concrete PropertyType type
|
||||
// is passed in, the explicit implementation doesn't get called, this ensures it does get called.
|
||||
|
||||
@@ -728,6 +728,8 @@ namespace Umbraco.Cms.Core.Services
|
||||
media.CreatorId = userId;
|
||||
}
|
||||
|
||||
media.WriterId = userId;
|
||||
|
||||
_mediaRepository.Save(media);
|
||||
scope.Notifications.Publish(new MediaSavedNotification(media, eventMessages).WithStateFrom(savingNotification));
|
||||
// TODO: See note about suppressing events in content service
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
@@ -305,10 +305,10 @@ namespace Umbraco.Cms.Core.Strings
|
||||
return text;
|
||||
}
|
||||
|
||||
private static string RemoveSurrogatePairs(string text)
|
||||
private string RemoveSurrogatePairs(string text)
|
||||
{
|
||||
var input = text.ToCharArray();
|
||||
var output = new char[input.Length];
|
||||
var input = text.AsSpan();
|
||||
Span<char> output = input.Length <= 1024 ? stackalloc char[input.Length] : new char[text.Length];
|
||||
var opos = 0;
|
||||
|
||||
for (var ipos = 0; ipos < input.Length; ipos++)
|
||||
@@ -325,7 +325,7 @@ namespace Umbraco.Cms.Core.Strings
|
||||
}
|
||||
}
|
||||
|
||||
return new string(output, 0, opos);
|
||||
return new string(output);
|
||||
}
|
||||
|
||||
// here was a subtle, ascii-optimized version of the cleaning code, and I was
|
||||
@@ -347,7 +347,8 @@ namespace Umbraco.Cms.Core.Strings
|
||||
|
||||
// it's faster to use an array than a StringBuilder
|
||||
var ilen = input.Length;
|
||||
var output = new char[ilen * 2]; // twice the length should be OK in all cases
|
||||
var totalSize = ilen * 2;
|
||||
Span<char> output = totalSize <= 1024 ? stackalloc char[totalSize] : new char[totalSize]; // twice the length should be OK in all cases
|
||||
|
||||
for (var i = 0; i < ilen; i++)
|
||||
{
|
||||
@@ -479,11 +480,11 @@ namespace Umbraco.Cms.Core.Strings
|
||||
throw new Exception("Invalid state.");
|
||||
}
|
||||
|
||||
return new string(output, 0, opos);
|
||||
return new string(output.Slice(0, opos));
|
||||
}
|
||||
|
||||
// note: supports surrogate pairs in input string
|
||||
internal void CopyTerm(string input, int ipos, char[] output, ref int opos, int len, CleanStringType caseType, string culture, bool isAcronym)
|
||||
internal void CopyTerm(string input, int ipos, Span<char> output, ref int opos, int len, CleanStringType caseType, string culture, bool isAcronym)
|
||||
{
|
||||
var term = input.Substring(ipos, len);
|
||||
CultureInfo cultureInfo = string.IsNullOrEmpty(culture) ? CultureInfo.InvariantCulture : CultureInfo.GetCultureInfo(culture);
|
||||
@@ -509,19 +510,19 @@ namespace Umbraco.Cms.Core.Strings
|
||||
//case CleanStringType.LowerCase:
|
||||
//case CleanStringType.UpperCase:
|
||||
case CleanStringType.Unchanged:
|
||||
term.CopyTo(0, output, opos, len);
|
||||
term.CopyTo(output.Slice(opos, len));
|
||||
opos += len;
|
||||
break;
|
||||
|
||||
case CleanStringType.LowerCase:
|
||||
term = term.ToLower(cultureInfo);
|
||||
term.CopyTo(0, output, opos, term.Length);
|
||||
term.CopyTo(output.Slice(opos, term.Length));
|
||||
opos += term.Length;
|
||||
break;
|
||||
|
||||
case CleanStringType.UpperCase:
|
||||
term = term.ToUpper(cultureInfo);
|
||||
term.CopyTo(0, output, opos, term.Length);
|
||||
term.CopyTo(output.Slice(opos, term.Length));
|
||||
opos += term.Length;
|
||||
break;
|
||||
|
||||
@@ -532,7 +533,7 @@ namespace Umbraco.Cms.Core.Strings
|
||||
{
|
||||
s = term.Substring(ipos, 2);
|
||||
s = opos == 0 ? s.ToLower(cultureInfo) : s.ToUpper(cultureInfo);
|
||||
s.CopyTo(0, output, opos, s.Length);
|
||||
s.CopyTo(output.Slice(opos, s.Length));
|
||||
opos += s.Length;
|
||||
i++; // surrogate pair len is 2
|
||||
}
|
||||
@@ -543,7 +544,7 @@ namespace Umbraco.Cms.Core.Strings
|
||||
if (len > i)
|
||||
{
|
||||
term = term.Substring(i).ToLower(cultureInfo);
|
||||
term.CopyTo(0, output, opos, term.Length);
|
||||
term.CopyTo(output.Slice(opos, term.Length));
|
||||
opos += term.Length;
|
||||
}
|
||||
break;
|
||||
@@ -555,7 +556,7 @@ namespace Umbraco.Cms.Core.Strings
|
||||
{
|
||||
s = term.Substring(ipos, 2);
|
||||
s = s.ToUpper(cultureInfo);
|
||||
s.CopyTo(0, output, opos, s.Length);
|
||||
s.CopyTo(output.Slice(opos, s.Length));
|
||||
opos += s.Length;
|
||||
i++; // surrogate pair len is 2
|
||||
}
|
||||
@@ -566,7 +567,7 @@ namespace Umbraco.Cms.Core.Strings
|
||||
if (len > i)
|
||||
{
|
||||
term = term.Substring(i).ToLower(cultureInfo);
|
||||
term.CopyTo(0, output, opos, term.Length);
|
||||
term.CopyTo(output.Slice(opos, term.Length));
|
||||
opos += term.Length;
|
||||
}
|
||||
break;
|
||||
@@ -578,7 +579,7 @@ namespace Umbraco.Cms.Core.Strings
|
||||
{
|
||||
s = term.Substring(ipos, 2);
|
||||
s = opos == 0 ? s : s.ToUpper(cultureInfo);
|
||||
s.CopyTo(0, output, opos, s.Length);
|
||||
s.CopyTo(output.Slice(opos, s.Length));
|
||||
opos += s.Length;
|
||||
i++; // surrogate pair len is 2
|
||||
}
|
||||
@@ -589,7 +590,7 @@ namespace Umbraco.Cms.Core.Strings
|
||||
if (len > i)
|
||||
{
|
||||
term = term.Substring(i);
|
||||
term.CopyTo(0, output, opos, term.Length);
|
||||
term.CopyTo(output.Slice(opos, term.Length));
|
||||
opos += term.Length;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -11,21 +11,27 @@ namespace Umbraco.Cms.Core.Strings;
|
||||
/// </remarks>
|
||||
public static class Utf8ToAsciiConverter
|
||||
{
|
||||
[Obsolete("Use ToAsciiString(ReadOnlySpan<char>..) instead")]
|
||||
public static string ToAsciiString(string text, char fail = '?')
|
||||
{
|
||||
return ToAsciiString(text.AsSpan(), fail);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an Utf8 string into an Ascii string.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to convert.</param>
|
||||
/// <param name="fail">The character to use to replace characters that cannot properly be converted.</param>
|
||||
/// <returns>The converted text.</returns>
|
||||
public static string ToAsciiString(string text, char fail = '?')
|
||||
public static string ToAsciiString(ReadOnlySpan<char> text, char fail = '?')
|
||||
{
|
||||
var input = text.ToCharArray();
|
||||
|
||||
// this is faster although it uses more memory
|
||||
// but... we should be filtering short strings only...
|
||||
var output = new char[input.Length * 3]; // *3 because of things such as OE
|
||||
var len = ToAscii(input, output, fail);
|
||||
return new string(output, 0, len);
|
||||
|
||||
var totalSize = text.Length * 3;
|
||||
Span<char> output = totalSize <= 1024 ? stackalloc char[totalSize] : new char[totalSize]; // *3 because of things such as OE
|
||||
var len = ToAscii(text, output, fail);
|
||||
return new string(output[..len]);
|
||||
|
||||
// var output = new StringBuilder(input.Length + 16); // default is 16, start with at least input length + little extra
|
||||
// ToAscii(input, output);
|
||||
@@ -66,7 +72,7 @@ public static class Utf8ToAsciiConverter
|
||||
/// <returns>The number of characters in the output array.</returns>
|
||||
/// <remarks>The caller must ensure that the output array is big enough.</remarks>
|
||||
/// <exception cref="OverflowException">The output array is not big enough.</exception>
|
||||
private static int ToAscii(char[] input, char[] output, char fail = '?')
|
||||
private static int ToAscii(ReadOnlySpan<char> input, Span<char> output, char fail = '?')
|
||||
{
|
||||
var opos = 0;
|
||||
|
||||
@@ -121,7 +127,7 @@ public static class Utf8ToAsciiConverter
|
||||
/// <para>Input should contain Utf8 characters exclusively and NOT Unicode.</para>
|
||||
/// <para>Removes controls, normalizes whitespaces, replaces symbols by '?'.</para>
|
||||
/// </remarks>
|
||||
private static void ToAscii(char[] input, int ipos, char[] output, ref int opos, char fail = '?')
|
||||
private static void ToAscii(ReadOnlySpan<char> input, int ipos, Span<char> output, ref int opos, char fail = '?')
|
||||
{
|
||||
var c = input[ipos];
|
||||
|
||||
|
||||
@@ -142,6 +142,9 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
|
||||
//update editor state to what is current
|
||||
editorState.set(args.content);
|
||||
|
||||
//needs to be manually set for infinite editing mode
|
||||
args.scope.isNew = args.content.id === 0 && args.scope.isNew;
|
||||
|
||||
return $q.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -796,6 +796,7 @@ input.umb-group-builder__group-sort-value {
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
|
||||
input.editor-label,
|
||||
textarea.editor-label {
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
}
|
||||
|
||||
.umb-editor-wrapper .umb-node-preview {
|
||||
.umb-property-editor--limit-width();
|
||||
word-break: break-word;
|
||||
.umb-property-editor--limit-width();
|
||||
}
|
||||
|
||||
.umb-node-preview:last-of-type {
|
||||
@@ -38,7 +39,6 @@
|
||||
|
||||
.umb-node-preview__content {
|
||||
flex: 1 1 auto;
|
||||
margin-right: 25px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
.umb-readonlyvalue {
|
||||
position:relative;
|
||||
.umb-readonlyvalue {
|
||||
position: relative;
|
||||
word-break: break-word;
|
||||
.umb-property-editor--limit-width();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
var vm = this;
|
||||
|
||||
const dataTypesCanBeChangedConfig = window.Umbraco.Sys.ServerVariables.umbracoSettings.dataTypesCanBeChanged;
|
||||
|
||||
|
||||
vm.allowChangeDataType = false;
|
||||
vm.changeDataTypeHelpTextIsVisible = false;
|
||||
vm.propertyTypeHasValues = false;
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
|
||||
<div class="umb-control-group -no-border" ng-if="!model.property.locked">
|
||||
<div class="control-group -no-margin">
|
||||
<textarea class="editor-label"
|
||||
<input type="text"
|
||||
class="editor-label"
|
||||
data-element="property-name"
|
||||
name="propertyLabel"
|
||||
ng-model="model.property.label"
|
||||
@@ -28,10 +29,8 @@
|
||||
placeholder="@placeholders_entername"
|
||||
umb-auto-focus
|
||||
focus-on-filled="true"
|
||||
umb-auto-resize
|
||||
required
|
||||
ng-keypress="vm.submitOnEnter($event)">
|
||||
</textarea>
|
||||
ng-keypress="vm.submitOnEnter($event)" />
|
||||
<div ng-messages="propertySettingsForm.propertyLabel.$error" show-validation-on-submit>
|
||||
<span class="umb-validation-label" ng-message="required">Required label</span>
|
||||
</div>
|
||||
@@ -47,11 +46,10 @@
|
||||
ng-model="model.property.description"
|
||||
localize="placeholder"
|
||||
placeholder="@placeholders_enterDescription"
|
||||
ng-keypress="vm.submitOnEnter($event)"
|
||||
umb-auto-resize>
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
|
||||
<ng-form name="dataTypeForm">
|
||||
<div class="umb-control-group control-group" ng-model="model.property.editor" name="selectedEditor" val-require-component ng-if="!model.property.locked">
|
||||
<umb-button
|
||||
@@ -82,7 +80,7 @@
|
||||
allow-change="vm.allowChangeDataType"
|
||||
on-change="vm.openDataTypePicker(model.property)">
|
||||
</umb-node-preview>
|
||||
|
||||
|
||||
<div ng-if="vm.changeDataTypeHelpTextIsVisible" class="mt2 umb-alert umb-alert--info">
|
||||
<localize key="contentTypeEditor_changeDataTypeHelpText">
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<ul role="tablist" class="umb-tabs-nav">
|
||||
<li class="umb-tab" ng-repeat="tab in vm.tabs | limitTo: vm.maxTabs" data-element="tab-{{tab.alias}}" ng-class="{'umb-tab--active': tab.active, 'umb-tab--error': valTab_tabHasError}" val-tab>
|
||||
<button class="btn-reset umb-tab-button" ng-click="vm.clickTab($event, tab)" role="tab" ng-disabled="tab.disabled" aria-selected="{{tab.active}}" type="button">
|
||||
<ul role="tablist" class="umb-tabs-nav">
|
||||
<li role="tab" aria-selected="{{tab.active}}" class="umb-tab" ng-repeat="tab in vm.tabs | limitTo: vm.maxTabs" data-element="tab-{{tab.alias}}" ng-class="{'umb-tab--active': tab.active, 'umb-tab--error': valTab_tabHasError}" val-tab>
|
||||
<button class="btn-reset umb-tab-button" ng-click="vm.clickTab($event, tab)" ng-disabled="tab.disabled" type="button">
|
||||
{{ tab.label }}
|
||||
<div ng-show="valTab_tabHasError && !tab.active" class="badge">!</div>
|
||||
</button>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<localize key="blockEditor_areaAliasHelp">When using GetBlockGridHTML() to render the Block Grid, the alias will be rendered in the markup as a 'data-area-alias' attribute. Use the alias attribute to target the element for the area. Example. .umb-block-grid__area[data-area-alias="MyAreaAlias"] { ... }</localize>
|
||||
</umb-property-info-button>
|
||||
<div class="controls">
|
||||
<input type="text" name="alias" ng-model="vm.area.alias" val-server="alias" style="width:100%" required umb-auto-focus/>
|
||||
<input type="text" name="alias" ng-model="vm.area.alias" val-server="alias" class="w-100" required umb-auto-focus/>
|
||||
</div>
|
||||
<div ng-messages="blockGridBlockConfigurationAreaForm.alias.$error" class="red">
|
||||
<div ng-message="alias">
|
||||
@@ -51,7 +51,7 @@
|
||||
<localize key="blockEditor_areaCreateLabelHelp">Override the label text for adding a new Block to this Area, Example: 'Add Widget'</localize>
|
||||
</umb-property-info-button>
|
||||
<div class="controls">
|
||||
<input type="text" name="createLabel" ng-model="vm.area.createLabel" style="width:100%" umb-auto-focus/>
|
||||
<input type="text" name="createLabel" ng-model="vm.area.createLabel" class="w-100" umb-auto-focus/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
3626
tests/Umbraco.Tests.Benchmarks/OldUtf8ToAsciiConverter.cs
Normal file
3626
tests/Umbraco.Tests.Benchmarks/OldUtf8ToAsciiConverter.cs
Normal file
File diff suppressed because it is too large
Load Diff
108
tests/Umbraco.Tests.Benchmarks/ShortStringHelperBenchmarks.cs
Normal file
108
tests/Umbraco.Tests.Benchmarks/ShortStringHelperBenchmarks.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Tests.Benchmarks.Config;
|
||||
|
||||
namespace Umbraco.Tests.Benchmarks;
|
||||
|
||||
[QuickRunWithMemoryDiagnoserConfig]
|
||||
public class ShortStringHelperBenchmarks
|
||||
{
|
||||
private DefaultShortStringHelper _shortStringHelper;
|
||||
|
||||
private string _input;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_shortStringHelper = new DefaultShortStringHelper(new DefaultShortStringHelperConfig());
|
||||
_input = "This is a 🎈 balloon";
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public void ToUrlSegment()
|
||||
{
|
||||
_shortStringHelper.CleanStringForUrlSegment(_input);
|
||||
}
|
||||
|
||||
/*[Benchmark(Baseline = true)]
|
||||
public string OldAsciString()
|
||||
{
|
||||
return OldUtf8ToAsciiConverter.ToAsciiString(_input);
|
||||
}
|
||||
|
||||
|
||||
[Benchmark]
|
||||
public string NewAsciString()
|
||||
{
|
||||
return Utf8ToAsciiConverter.ToAsciiString(_input);
|
||||
}*/
|
||||
|
||||
#region SurrogatePairs
|
||||
|
||||
/*[Benchmark(Baseline = true)]
|
||||
public string RemoveSurrogatePairs()
|
||||
{
|
||||
var input = _input.ToCharArray();
|
||||
var output = new char[input.Length];
|
||||
var opos = 0;
|
||||
|
||||
for (var ipos = 0; ipos < input.Length; ipos++)
|
||||
{
|
||||
var c = input[ipos];
|
||||
if (char.IsSurrogate(c)) // ignore high surrogate
|
||||
{
|
||||
ipos++; // and skip low surrogate
|
||||
output[opos++] = '?';
|
||||
}
|
||||
else
|
||||
{
|
||||
output[opos++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
return new string(output, 0, opos);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public string RemoveNewSurrogatePairs()
|
||||
{
|
||||
var input = _input.AsSpan();
|
||||
Span<char> output = input.Length <= 1024 ? stackalloc char[input.Length] : new char[input.Length];
|
||||
var opos = 0;
|
||||
|
||||
for (var ipos = 0; ipos < input.Length; ipos++)
|
||||
{
|
||||
var c = input[ipos];
|
||||
if (char.IsSurrogate(c)) // ignore high surrogate
|
||||
{
|
||||
ipos++; // and skip low surrogate
|
||||
output[opos++] = '?';
|
||||
}
|
||||
else
|
||||
{
|
||||
output[opos++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
return new string(output);
|
||||
}*/
|
||||
|
||||
#endregion
|
||||
|
||||
//| Method | Mean | Error | StdDev | Ratio | Gen 0 | Allocated |
|
||||
//|-----------------------------------:|---------:|---------:|--------:|------:|-------:|----------:|
|
||||
//| ToUrlSegment | 464.2 ns | 34.88 ns | 1.91 ns | 1.00 | 0.1627 | 512 B |
|
||||
//| ToUrlSegment (With below changes) | 455.7 ns | 26.83 ns | 1.47 ns | 1.00 | 0.1182 | 384 B |
|
||||
//| ToUrlSegment(CleanCodeString change| 420.6 ns | 64.06 ns | 3.51 ns | 1.00 | 0.0856 | 280 B |
|
||||
|
||||
//| Method | Mean | Error | StdDev | Ratio | Gen 0 | Allocated |
|
||||
//|------------------------ |---------:|----------:|---------:|------:|-------:|----------:|
|
||||
//| RemoveSurrogatePairs | 70.75 ns | 15.307 ns | 0.839 ns | 1.00 | 0.0610 | 192 B |
|
||||
//| RemoveNewSurrogatePairs | 58.44 ns | 7.297 ns | 0.400 ns | 0.83 | 0.0198 | 64 B |
|
||||
|
||||
//| Method | Mean | Error | StdDev | Ratio | Gen 0 | Allocated |
|
||||
//|-------------- |---------:|---------:|--------:|------:|-------:|----------:|
|
||||
//| OldAsciString | 181.4 ns | 11.50 ns | 0.63 ns | 1.00 | 0.0851 | 272 B |
|
||||
//| NewAsciString | 180.7 ns | 5.35 ns | 0.29 ns | 1.00 | 0.0450 | 64 B |
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Umbraco.Tests.Benchmarks.Config;
|
||||
|
||||
namespace Umbraco.Tests.Benchmarks;
|
||||
|
||||
[QuickRunWithMemoryDiagnoserConfig]
|
||||
public class StringReplaceFirstBenchmarks
|
||||
{
|
||||
[Params("Test string",
|
||||
"This is a test string that contains multiple test entries",
|
||||
"This is a string where the searched value is very far back. The system needs to go through all of this code before it reaches the test")]
|
||||
public string Text { get; set; }
|
||||
public string Search { get; set; }
|
||||
public string Replace { get; set; }
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
Search = "test";
|
||||
Replace = "release";
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true, Description = "Replace first w/ substring")]
|
||||
public string SubstringReplaceFirst()
|
||||
{
|
||||
var pos = Text.IndexOf(Search, StringComparison.InvariantCulture);
|
||||
|
||||
if (pos < 0)
|
||||
{
|
||||
return Text;
|
||||
}
|
||||
|
||||
return Text.Substring(0, pos) + Replace + Text.Substring(pos + Search.Length);
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Replace first w/ span")]
|
||||
public string SpanReplaceFirst()
|
||||
{
|
||||
var spanText = Text.AsSpan();
|
||||
var pos = spanText.IndexOf(Search, StringComparison.InvariantCulture);
|
||||
|
||||
if (pos < 0)
|
||||
{
|
||||
return Text;
|
||||
}
|
||||
|
||||
return string.Concat(spanText[..pos], Replace.AsSpan(), spanText[(pos + Search.Length)..]);
|
||||
}
|
||||
|
||||
//| Method | Text | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Allocated |
|
||||
//|----------------------------- |--------------------- |----------:|---------:|---------:|------:|--------:|-------:|----------:|
|
||||
//| 'Replace first w/ substring' | Test string | 46.08 ns | 25.83 ns | 1.416 ns | 1.00 | 0.00 | - | - |
|
||||
//| 'Replace first w/ span' | Test string | 38.59 ns | 19.46 ns | 1.067 ns | 0.84 | 0.05 | - | - |
|
||||
//| | | | | | | | | |
|
||||
//| 'Replace first w/ substring' | This(...)test[134] | 407.89 ns | 52.08 ns | 2.855 ns | 1.00 | 0.00 | 0.1833 | 584 B |
|
||||
//| 'Replace first w/ span' | This(...)test[134] | 372.99 ns | 58.38 ns | 3.200 ns | 0.91 | 0.01 | 0.0941 | 296 B |
|
||||
//| | | | | | | | | |
|
||||
//| 'Replace first w/ substring' | This(...)tries[57] | 113.16 ns | 27.95 ns | 1.532 ns | 1.00 | 0.00 | 0.0961 | 304 B |
|
||||
//| 'Replace first w/ span' | This(...)tries[57] | 76.57 ns | 17.86 ns | 0.979 ns | 0.68 | 0.01 | 0.0455 | 144 B |
|
||||
}
|
||||
@@ -74,6 +74,7 @@ public class StringReplaceManyBenchmarks
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
short text, short replacement:
|
||||
|
||||
@@ -255,7 +255,7 @@ public class CreatedPackagesRepositoryTests : UmbracoIntegrationTest
|
||||
Assert.AreEqual(test, mediaEntry.Name);
|
||||
Assert.IsNotNull(zipArchive.GetEntry("package.xml"));
|
||||
Assert.AreEqual(
|
||||
$"<MediaItems><MediaSet><testImage id=\"{m1.Id}\" key=\"{m1.Key}\" parentID=\"-1\" level=\"1\" creatorID=\"-1\" sortOrder=\"0\" createDate=\"{m1.CreateDate:s}\" updateDate=\"{m1.UpdateDate:s}\" nodeName=\"Test File\" urlName=\"test-file\" path=\"{m1.Path}\" isDoc=\"\" nodeType=\"{mt.Id}\" nodeTypeAlias=\"testImage\" writerName=\"\" writerID=\"0\" udi=\"{m1.GetUdi()}\" mediaFilePath=\"/media/test-file.txt\"><umbracoFile><![CDATA[/media/test-file.txt]]></umbracoFile><umbracoBytes><![CDATA[100]]></umbracoBytes><umbracoExtension><![CDATA[png]]></umbracoExtension></testImage></MediaSet></MediaItems>",
|
||||
$"<MediaItems><MediaSet><testImage id=\"{m1.Id}\" key=\"{m1.Key}\" parentID=\"-1\" level=\"1\" creatorID=\"-1\" sortOrder=\"0\" createDate=\"{m1.CreateDate:s}\" updateDate=\"{m1.UpdateDate:s}\" nodeName=\"Test File\" urlName=\"test-file\" path=\"{m1.Path}\" isDoc=\"\" nodeType=\"{mt.Id}\" nodeTypeAlias=\"testImage\" writerName=\"Administrator\" writerID=\"-1\" udi=\"{m1.GetUdi()}\" mediaFilePath=\"/media/test-file.txt\"><umbracoFile><![CDATA[/media/test-file.txt]]></umbracoFile><umbracoBytes><![CDATA[100]]></umbracoBytes><umbracoExtension><![CDATA[png]]></umbracoExtension></testImage></MediaSet></MediaItems>",
|
||||
packageXml.Element("umbPackage").Element("MediaItems").ToString(SaveOptions.DisableFormatting));
|
||||
Assert.AreEqual(2, zipArchive.Entries.Count());
|
||||
Assert.AreEqual(ZipArchiveMode.Read, zipArchive.Mode);
|
||||
|
||||
@@ -326,6 +326,14 @@ public class StringExtensionsTests
|
||||
Assert.AreEqual(expected, output);
|
||||
}
|
||||
|
||||
[TestCase("test to test", "test", "release", "release to test")]
|
||||
[TestCase("nothing to do", "test", "release", "nothing to do")]
|
||||
public void ReplaceFirst(string input, string search, string replacement, string expected)
|
||||
{
|
||||
var output = input.ReplaceFirst(search, replacement);
|
||||
Assert.AreEqual(expected, output);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsFullPath()
|
||||
{
|
||||
|
||||
@@ -12,7 +12,5 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Umbraco.Core\Umbraco.Core.csproj" />
|
||||
<PackageReference Include="Umbraco.Deploy.Core" Version="10.1.3" />
|
||||
<PackageReference Include="Umbraco.Forms.Core" Version="10.2.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user