Skip to content

Commit

Permalink
Improve LazyString + add test
Browse files Browse the repository at this point in the history
  • Loading branch information
adams85 committed Aug 2, 2024
1 parent 75ea32b commit e9d2731
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 15 deletions.
22 changes: 22 additions & 0 deletions src/ConfigCat.Client.Tests/UtilsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,26 @@ public void ModelHelper_SetEnum_Works(SettingType enumValue)
Assert.ThrowsException<ArgumentOutOfRangeException>(() => ModelHelper.SetEnum(ref field, enumValue));
}
}

[DataTestMethod]
[DataRow(null, false, true, null)]
[DataRow("abc", false, true, "abc")]
[DataRow("abc", null, false, "abc")]
[DataRow("abc", new object?[0], false, "abc")]
[DataRow("abc{0}{1}{2}", new object?[] { 0.1, null, 23 }, false, "abc0.123")]
public void LazyString_Value_Works(string? valueOrFormat, object args, bool expectedIsValueCreated, string expectedValue)
{
var lazyString = args is false ? new LazyString(valueOrFormat) : new LazyString(valueOrFormat!, (object?[]?)args);

Assert.AreEqual(expectedIsValueCreated, lazyString.IsValueCreated);

var value = lazyString.Value;
Assert.AreEqual(expectedValue, value);

Assert.IsTrue(lazyString.IsValueCreated);

Assert.AreSame(value, lazyString.Value);

Assert.AreSame(expectedValue is not null ? value : string.Empty, lazyString.ToString());
}
}
2 changes: 1 addition & 1 deletion src/ConfigCatClient/Evaluation/EvaluationDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private protected EvaluationDetails(string key)
/// </summary>
public string? ErrorMessage
{
get => this.errorMessage.ToString();
get => this.errorMessage.Value;
set => this.errorMessage = value;
}

Expand Down
5 changes: 4 additions & 1 deletion src/ConfigCatClient/FetchResult.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using ConfigCat.Client.Utils;

Expand All @@ -20,7 +22,8 @@ public static FetchResult NotModified(ProjectConfig config)

public static FetchResult Failure(ProjectConfig config, RefreshErrorCode errorCode, LazyString errorMessage, Exception? errorException = null)
{
return new FetchResult(config, errorCode, errorMessage.Value ?? (object)errorMessage, errorException);
Debug.Assert(!EqualityComparer<LazyString>.Default.Equals(errorMessage, default));
return new FetchResult(config, errorCode, errorMessage.IsValueCreated ? errorMessage.Value : (object)errorMessage, errorException);
}

private readonly object? errorMessageOrToken; // either null or a string or a boxed LazyString or NotModifiedToken
Expand Down
40 changes: 27 additions & 13 deletions src/ConfigCatClient/Utils/LazyString.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
using System.Diagnostics;
using System.Globalization;

namespace ConfigCat.Client.Utils;

/// <summary>
/// Defers string formatting until the formatted value is actually needed.
/// </summary>
/// <remarks>
/// It roughly achieves what <c>new Lazy&lt;string&gt;(() => string.Format(CultureInfo.InvariantCulture, format, args), isThreadSafe: false)</c> does
/// but without extra heap memory allocations.
/// </remarks>
internal struct LazyString
{
private readonly string? format;
Expand All @@ -19,24 +27,30 @@ public LazyString(string format, params object?[]? args)
this.argsOrValue = args ?? ArrayUtils.EmptyArray<string>();
}

public string? Value => this.argsOrValue as string;

public override string? ToString()
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public string? Value
{
var argsOrValue = this.argsOrValue;
if (argsOrValue is null)
get
{
return null;
var argsOrValue = this.argsOrValue;
if (argsOrValue is null)
{
return null;
}

if (argsOrValue is string value)
{
return value;
}

this.argsOrValue = value = string.Format(CultureInfo.InvariantCulture, this.format!, (object?[])argsOrValue);
return value;
}
}

if (argsOrValue is not string value)
{
var args = (object?[])argsOrValue;
this.argsOrValue = value = string.Format(CultureInfo.InvariantCulture, this.format!, args);
}
public bool IsValueCreated => this.argsOrValue is null or string;

return value;
}
public override string ToString() => Value ?? string.Empty;

public static implicit operator LazyString(string? value) => new LazyString(value);
}

0 comments on commit e9d2731

Please sign in to comment.