Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ilia-kosenkov/backports
Backporting some Span APIs to .NET Standard 2.0
https://github.com/ilia-kosenkov/backports
csharp formatting parsing
Last synced: 14 days ago
JSON representation
Backporting some Span APIs to .NET Standard 2.0
- Host: GitHub
- URL: https://github.com/ilia-kosenkov/backports
- Owner: Ilia-Kosenkov
- License: mit
- Created: 2020-10-19T23:43:33.000Z (about 4 years ago)
- Default Branch: master
- Last Pushed: 2021-03-01T13:25:30.000Z (almost 4 years ago)
- Last Synced: 2024-10-28T04:59:19.927Z (2 months ago)
- Topics: csharp, formatting, parsing
- Language: C#
- Homepage:
- Size: 292 KB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
![Build & Test](https://github.com/Ilia-Kosenkov/Backports/workflows/Build%20&%20Test/badge.svg?branch=master)
# Backports
Backporting some Span APIs to .NET Standard2.0*This repository contains code from [.NET runtime](https://github.com/dotnet/runtime) licensed by .NET Foundation under MIT.*
# Why backport APIs?
.NET Standard 2.1, .NET Core 3.x, 5.0 provide a rich set of APIs that naturally work with Spans. Some of these APIs are available in .NET Standard 2.0, and therefore can be used from .NET 4.8 projects (aka legacy).However, there are very useful APIs that are unavailable in early versions of .NET Standard, including `.Parse/TryParse`. These static methods allow number parsing directly from `ReadOnlySpan`, meaning numbers can be parsed from a heap array of `char`s, a regular `string`, or even from `stackalloc`ed/borrowed buffer.
The goal of this project is to extract part of .NET libraries available in .NET 5.0 and implement them using tools available in .NET Standard 2.0.
With no tools to extend existing types with static methods, the best solution is to introduce a new set of simple functions.
`bool Numbers.TryParse(ReadOnlySpan input, out T value)` attempts to parse `T` from `char` using default for a given `T` settings. `T` is naturally limited to `unmanaged` and method throws for non-primitive types.
This (and other similar) generic utilizes `JIT` capability of eliminating unused branches when invoking with a concrete `T`.
The method is compiled differently depending on the target framework. For .NET Standard 2.1 it defaults to BCL's implementation, while for .NET Standard 2.0 it uses brrowed code fragments to mimick .NET 5+ behavior.The goal is to achieve performance at least as good as native `string`-based methods and eliminate unnecessary allocations of e.g. `string.Substring()` with the help of `Span.Slice` when parsing from large text blocks.
# Getting rid of native pointers
Original code heavily relies on native pointers and unsafe context. While `ref` structs (like `Span`) have been introduced, they are sometimes underused and are immediately converted into native pointers within `fixed` statement. Stack allocations also occur unsafely, and reuslting pointer together with the size of hte allocated block is passed around.
This issues can be (at least partially) eliminated using managed pointers and corresponding `Unsafe` operations. In some cases this may yield performance improvements (though unlikely in case of this port), because elimination of pinned memory chunks (within `fixed`) helps GC to deal with the memory.
Managed pointers come at a price. There is no (safe) notion of pointer to a pointer (to a pointer), which can be required sometimes.
A common use case of native pointers is iterating over an array and perfoming assignments in a form of
```csharp
byte* p = GetPtr();
while(true)
*(++p) = 0;
```
In the managed pointer world, this results into weird constructs, similar to the following one
```csharp
ref var p = ref GetRef();
while(true)
(p = ref Increment(ref p)) = 0;
/// ....
static ref T Increment(ref T value) where T : unmanaged => ref Unsafe.Add(ref value, 1);
```
At the same time, `*(p++)` has no one-line equivalent in managed pointers, and transforms into two assignments.Overall, using C# 7+ feature improves the readability and clarity of the code. Certain Asserts that check incoming pointers against `null` are no longer needed, as `ref T` should not be `null` in safe context (though, `unsafe {ref byte p = ref Unsafe.As(null);}` is possible).
# Incorrect formatting
Legacy framework exhibits substantially less elegant methods of numbers formatting. As a result, ported from .NET Core implementation does not agree with the results of legacy framework's `.ToString(fmt)` methods. Thus, using backported methods can not only save one-two string allocations, but also produce correct results when formatting numbers.