Skip to content

Releases: axuno/SmartFormat

v3.6.0

17 Mar 16:39
daaa16d
Compare
Choose a tag to compare

Release Notes

Thread Safety Enhancements

  1. Parser:

    • The parsing logic in Parser has been refactored by replacing stateful instance variables.
    • The Parser.ParseFormat(...) method is now thread-safe, ensuring safe operations in multi-threaded environments.
  2. SmartFormatter:

    • All SmartFormatter.Format... methods are thread-safe.
    • Removed the ThreadStatic attribute from the Smart.Default instance of SmartFormatter.
    • Added parallel unit tests to ensure thread-safe operations with shared SmartFormatter instances using different Smart.Extensions.
    • Updated documentation in Parser, Smart, and SmartFormatter classes to clarify the thread safety of methods.

Also see further remarks regarding thread-safety in the Wiki

Heads Up

  • Removal of ThreadStatic Attribute for Smart.Default:
    • The ThreadStatic attribute for the Smart.Default instance of SmartFormatter has been removed.
    • This change addresses user feedback regarding their lack of favor and the increased GC pressure it caused in multi-threaded environments like ASP.NET Core.
    • The removal is intended to enhance usability and performance in multi-threaded environments. It may, however, break existing code.

User Feedback

image

Sample Usage: Using One SmartFormatter Instance with Several Threads in Parallel

The following example demonstrates how to use a single SmartFormatter instance with multiple threads in parallel. This ensures thread-safe operations and efficient resource utilization.

using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SmartFormat;

public class Program
{
    public static void Main()
    {
        // Create a single instance of SmartFormatter
        var smartFormatter = Smart.CreateDefaultSmartFormat();

        // Concurrent dictionary to store results
        var results = new ConcurrentDictionary<long, string>();
        var options = new ParallelOptions { MaxDegreeOfParallelism = 100 };

        // Run parallel tasks
        Parallel.For(0L, 1000, options, i =>
        {
            // Re-use the same SmartFormatter instance, where the Format method is thread-safe.
            // The static Smart.Format can be used as well, producing the same results
            results.TryAdd(i, smartFormatter.Format("{0:D3}", i));
        });

        // Output results
        var sortedResult = results.OrderBy(r => r.Value).ToList();
        foreach (var result in sortedResult)
        {
            Console.WriteLine(result.Value);
        }
    }
}

What's Changed in Detail

  • Refactor parsing for thread safety in #467
  • Update appveyor api key for nuget in #468
  • Shift CI Publishing Workflow to GitHub Actions in #469
  • Move code quality CI to Github Action CodeQuality.yml in #470
  • Make SmartFormatter.Format(...) methods thread-safe in #473
  • Bump version to v3.6.0 in #474

Full Changelog: v3.5.3...v3.6.0

v3.5.3

17 Jan 08:29
423466b
Compare
Choose a tag to compare

What's Changed

  • Add support for specifying separator between units when using TimeFormatter by @hakksor in #459
  • Bump version to v3.5.3 in #463

Example:

TimeFormatter allows for nested formats:

var ci = CultureInfo.GetCultureInfo("en");

// Using standard:
_ = Smart.Format(ci, "{1:time:", new TimeSpan(1,1,1,1,1));
// Output: "1 day 1 hour 1 minute 1 second"

// Using ListFormatter:
_ = Smart.Format(ci, "{1:time: {:list:|, | and }}", new TimeSpan(1,1,1,1,1));
// Output: "1 day, 1 hour, 1 minute and 1 second"

New Contributors

Full Changelog: v3.5.2...v3.5.3

v3.5.2

18 Dec 16:42
2a0c495
Compare
Choose a tag to compare

What's Changed

  • Enhancement: Enable chaining from StringBuilder.AppendSmart() and StringBuilder.AppendLineSmart() by @Klikini in #452
  • [Fix Conditional Formatter] Use invariant decimal parsing by @karljj1 in #456
  • Bump version to v3.5.2 in #457

Backstage Changes

  • Update appveyor build and test scripts for cross-platform compatibility in #446
  • Fix: Creation of AltCover report for Codecov in #447 and #455
  • Update package versions in project files in #453
  • Update NUnit v4.2.2 to v4.3.0 in #457

New Contributors

Full Changelog: v3.5.1...v3.5.2

v3.5.1

11 Oct 22:43
e3f7b5c
Compare
Choose a tag to compare

What's Changed

  • Microsoft Security Advisory CVE-2024-43485 in #442
  • Update dependencies in SmartFormat.Tests in #443
  • Bump version to v3.5.1 in #444

Full Changelog: v3.5.0...v3.5.1

v3.5.0

29 Jul 19:45
c097ea6
Compare
Choose a tag to compare

What's Changed

Feature: Add interface IFormattingExtensionsToggle to skip formatting (#436)

  • Added the IFormattingExtensionsToggle interface to allow skipping formatting by IFormatter extensions.
  • This interface is primarily used by ISource extensions that receive it with the ISelectorInfo parameter.
  • By setting IFormattingExtensionsToggle.DisableFormattingExtensions to true, formatting can be skipped. This can be useful when the ISource found a value in ISource.TryEvaluateSelector where default formatting cannot reasonably be done.

Security: Update of System.Text.Json (#435)

  • Bumped System.Text.Json to v8.0.4
  • Fixed a vulnerability in .NET when calling the JsonSerializer.DeserializeAsyncEnumerable method against an untrusted input using System.Text.Json, which could result in Denial of Service.

Feature: Implement ISpanFormattable for DefaultFormatter (#434)

  • Implemented ISpanFormattable for DefaultFormatter
  • ISpanFormattable is 5% faster than IFormattable, with 24% less allocations
     // Performance test case
     Smart.FormatInto(output, null, _placeholder0005Format, 1234567.890123f, 1234567.890123f, 1234567.890123f, 1234567.890123f, 1234567.890123f);
     BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.3737/23H2/2023Update/SunValley3)
     13th Gen Intel Core i7-13700K, 1 CPU, 24 logical and 16 physical cores
     .NET SDK 8.0.302
       [Host]   : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2
       .NET 8.0 : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2
    
     Job=.NET 8.0  Runtime=.NET 8.0
    
     | Method                   | N     | Mean          | Error       | StdDev      | Ratio | RatioSD | Gen0       | Allocated    | Alloc Ratio |
     |------------------------- |------ |--------------:|------------:|------------:|------:|--------:|-----------:|-------------:|------------:|
     | ISpanFormattable         | 100   |     74.502 us |   0.3693 us |   0.3273 us |  8.52 |    0.05 |     4.0283 |      62.5 KB |        2.76 |
     | IFormattable             | 100   |     77.927 us |   0.5760 us |   0.5388 us |  8.91 |    0.07 |     5.2490 |      82.0 KB |        3.62 |
    

Feature: ReflectionSource (#426)

  • Made ReflectionSource.TypeCache static for better performance.
  • If the static cache has undesired effects on your code logic, consider to disable the cache (ReflectionSource.IsTypeCacheEnabled = false).
  • Implemented a mechanism to control the size of ReflectionSource.TypeCache and remove the oldest item first.

Enhancement: DictionarySource (#426)

  • Dynamics in DictionarySource now use case-sensitivity setting
  • Cache for IReadOnlyDictionary now has instance scope

Refactor: Split off classes Registry and Evaluator from SmartFormatter (#424)

SmartFormatter

  • Separated members for handling ISource and IFormatter extensions into internal class Registry
  • Separated members for evaluating formats into internal class Evaluator
  • Existing members of SmartFormatter remain unchanged and are not yet marked as obsolete

ZCharArray

  • Added a lightweight, thread-safe container that rents a buffer from an ArrayPool<char> and returns it when disposed
  • Simplifies passing around the buffer without intermediate memory allocations
  • ZCharArray contains most frequently used methods for writing data into the underlying buffer
  • Used in FormattingInfo methods (see below) for low memory allocation

FormattingInfo

Added methods useful in custom IFormatters:

  • public ZCharArray FormattingInfo.FormatAsSpan(IFormatProvider, Format, object?): Works like SmartFormatter.Format(...) for the specified Format
  • public ZCharArray FormattingInfo.FormatAsSpan(IFormatProvider, Placeholder, object?): Gets the value for the Placeholder and applies its Format
  • public bool TryGetValue(Placeholder, out object?): Tries to get the value for a Placeholder

Evaluator

  • Internal class that supplies the methods for evaluating Placeholder and Format objects
  • Contains the methods that FormattingInfo uses to evaluate Placeholder and Format objects
  • Has other methods formerly included in SmartFormatter that are now moved to Evaluator
  • Includes a partial class for handling events in all steps of the evaluation process

Registry

  • Internal class that contains all methods for handling ISource and IFormatter extensions that have been moved from SmartFormatter

Feature: Format.HasNested checks for existing Placeholder in Items (#416)

  • Added Format.HasNested property that checks Items for existing Placeholder

Test: Add unit test for nested scope (#404)

  • Added unit test for nested scope

Enhancement: Ensure Format instances get returned to object pool (#402)

  • Ensured that Format instances get returned to object pool

Refactor: Internal SmartFormat.Pooling classes (#401)

  • Simplified pooling classes and made methods and names more consistent

Chore: Remove unused performance test projects (#398)

  • Removed unused performance test projects

Fix: Returning a StringBuilder exceeding default capacity to StringBuilderPool (#397)

  • Fixed an issue where returning a StringBuilder exceeding the default capacity to StringBuilderPool would throw an exception.

Refactor: Remove code duplications in SmartFormatter (#396)

  • Removed code duplications in SmartFormatter
  • Unified preprocessor directives

Chore: Remove redundant DependsOnTargets from SmartFormat.csproj (#394)

  • Removed redundant DependsOnTargets from SmartFormat.csproj

Full Changelog: v3.4.0...v3.5.0

v3.4.0

15 May 20:14
44165bb
Compare
Choose a tag to compare

What's Changed

.NET Framework 4.6.2

SmartFormat has transitioned to using .NET Framework 4.6.2 (net462) as its target framework, replacing the now unsupported .NET Framework 4.6.1 (net461). The support for net461 ended in 2022, while net462 will continue to receive support until January 2027.

Switching to a different patch version of a target framework is generally not considered a breaking change. This is referred to as an in-place update by Microsoft. In the case of SmartFormat, we have not encountered any runtime issues with the existing API after this transition.

The same holds true for the new netstandard2.0 reference to ZString.

.NET 6.0 and .NET 8.0

SmartFormat has expanded its compatibility by adding .NET 6.0 (net60) and .NET 8.0 (net80) as additional target frameworks. You can find details about their end of support here.

All Target Frameworks

We have removed the SmartFormat.ZString assembly and replaced it with a reference to the ZString package. This change does not affect the API.

Commits in detail

  • Fix: Update nuget api key in #378
  • Target Framework Updates in #389 (merged the version/3.4.0 branch)

Thanks to @thompson-tomo for his first contribution with #377

Full Changelog: v3.3.2...v3.4.0

v3.3.2

29 Jan 18:38
245d3b0
Compare
Choose a tag to compare

Reasoning for the Update

  • Regression caused by #368
  • If there are namespace collisions with Cysharp.Text using v3.3.1 please update to v3.2.2

What's Changed

  • Change all public types in namespace Cysharp.Text to internal in #372
  • Stop namespace collisions with Cysharp.ZString nuget package
  • Affects: class, struct, interface, delegate, enum
  • using https://github.com/zzzprojects/findandreplace on command line, so we can automate this step:
    "fnr.exe" --cl --dir "SmartFormat.ZString\repo\src\ZString" --fileMask ".cs,.tt" --includeSubDirectories --caseSensitive --useRegEx --find "public (?=.*(class |struct |enum |interface |delegate ))" --replace "internal "
  • Add a unit test to get an alert when Cysharp.Text objects are public (e.g. after an update)
  • Bump version to v3.3.2

Full Changelog: v3.3.1...v3.3.2

v3.3.1

14 Jan 22:51
d217ceb
Compare
Choose a tag to compare

Fix

PluralRule for DualFromZeroToTwo: Now a value of 2 is covered and will not throw. Frend is one of the affected languages. Closes #369 in #370

Enhancement

Dictionary<string, PluralRuleDelegate> PluralRule.IsoLangToDelegate holds delegates with the pluralization rule per language. Changing a value of this dictionary will change the pluralization rules globally. This is not recommended, but possible. After a change calling PluralRules.RestoreDefault() will restore the default rules.

Other Changes

  • Unit tests: Convert NUnit Classic Assert to Constraint Model in #364
  • Bump NUnit package reference to v4.0.1 in #367
  • Linq optimizations in #366
  • Update package Cysharp.ZString to v2.5.1 in #368
  • Bump version to v3.3.1 in #371

Full Changelog: v3.3.0...v3.3.1

v3.3.0

25 Sep 19:09
a9b0fb3
Compare
Choose a tag to compare

What's Changed

  • Add support for nested formats in LocalizationFormatter by @zacateras in #350. This is useful, if the string to localize contains a SmartFormat placeholder instead of a pure text. Example: If the format is "{:L:{ProductType}}", the ProductType placeholder will be replaced with the variable content "pen". "pen" will in turn be localiced to "bic" for the FR locale.
  • DictionarySource has an option to evaluate IReadOnlyDictionary<TKey,TValue> sources by @axunonb in #353. To enable, set DictionarySource.IsIReadOnlyDictionarySupported to true (default is false). This is for types that only implement IReadOnlyDictionary<TKey,TValue>, but not IDictionary.
  • Bump version to v3.3.0 in #356

New Contributors

Full Changelog: v3.2.2...v3.3.0

v3.2.2

03 Aug 15:31
e98917a
Compare
Choose a tag to compare

What's Changed

Fix

  • PluralLocalizationFormatter does not treat a numeric string as valid argument (resolves #345, restore behavior of v3.1.0 and before) in #346

  • Bump version to v3.2.2

    Meaning: Smart.Format("{0:{}|is null or empty}", "1234"); will not implicitly invoke PluralLocalizationFormatter but instead ConditionalFormatter. So the result is "1234" as to be expected. A string argument to PluralLocalizationFormatter is never accepted, even if it could be converted to a number.

Chore

  • Update appveyor and github CI scripts
  • Update change log for v3.2.1 in #331
  • Update NuGet API Key in #335
  • Update link to pluralization rules we use as a reference in #338
  • Add unit test for DateOnly and TimeOnly types (NET6.0+) in #341

Full Changelog: v3.2.1...v3.2.2