• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

neon-sunset / U8String / 5872154557

pending completion
5872154557

push

github

neon-sunset
feat: basic .Trim() implementation

105 of 740 branches covered (14.19%)

Branch coverage included in aggregate %.

38 of 38 new or added lines in 5 files covered. (100.0%)

372 of 1597 relevant lines covered (23.29%)

445.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

42.5
/src/U8String.cs
1
using System.ComponentModel;
2
using System.Diagnostics;
3
using System.Diagnostics.CodeAnalysis;
4
using System.Runtime.InteropServices;
5
using System.Text;
6
using System.Text.Json.Serialization;
7
using System.Text.Unicode;
8
using U8Primitives.Serialization;
9

10
#pragma warning disable IDE1006 // Naming Styles. Why: Exposing internal fields for perf.
11
namespace U8Primitives;
12

13
internal readonly struct U8Range
14
{
15
    internal readonly int Offset;
16
    internal readonly int Length;
17

18
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
19
    public U8Range(int offset, int length)
20
    {
21
        Debug.Assert((uint)offset <= int.MaxValue);
22
        Debug.Assert((uint)length <= int.MaxValue);
23

24
        Offset = offset;
40✔
25
        Length = length;
40✔
26
    }
40✔
27
}
28

29
/// <summary>
30
/// Represents a UTF-8 encoded string.
31
/// </summary>
32
/// <remarks>
33
/// <para>U8String is an immutable value type that represents a UTF-8 encoded string.</para>
34
/// <para>It stores UTF-8 code units in the underlying buffer, and provides methods
35
/// for manipulating and accessing the string content. It can be created from or converted
36
/// to a string or a span of bytes, as long as the data is valid and convertible to UTF-8.</para>
37
/// <para>U8String slicing methods are non-copying and return a new U8String that
38
/// references a portion of the original data. Methods which manipulate the
39
/// instances of U8String ensure that the resulting U8String is well-formed and valid UTF-8,
40
/// unless specified otherwise. If an operation would produce invalid UTF-8, an exception is thrown.</para>
41
/// <para>By default, U8String is indexed by the underlying UTF-8 bytes but offers alternate Rune and Char projections.</para>
42
/// </remarks>
43
[DebuggerDisplay("{ToString()}")]
44
[JsonConverter(typeof(U8StringJsonConverter))]
45
[CollectionBuilder(typeof(U8String), nameof(Create))]
46
public readonly partial struct U8String :
47
    IEquatable<U8String>,
48
    IEquatable<U8String?>,
49
    IEquatable<byte[]?>,
50
    IComparable<U8String>,
51
    IComparable<U8String?>,
52
    IComparable<byte[]?>,
53
    IList<byte>,
54
    ICloneable,
55
    ISpanParsable<U8String>,
56
    ISpanFormattable,
57
    IUtf8SpanParsable<U8String>,
58
    IUtf8SpanFormattable
59
{
60
    /// <summary>
61
    /// Represents an empty <see cref="U8String"/>.
62
    /// </summary>
63
    /// <remarks>
64
    /// Functionally equivalent to <see langword="default(U8String)"/>.
65
    /// </remarks>
66
    public static U8String Empty => default;
×
67

68
    internal readonly byte[]? _value;
69
    internal readonly U8Range _inner;
70

71
    internal int Offset
72
    {
73
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
74
        get => _inner.Offset;
1,356✔
75
    }
76

77
    /// <summary>
78
    /// The number of UTF-8 bytes in the current <see cref="U8String"/>.
79
    /// </summary>
80
    /// <returns>The number of UTF-8 bytes.</returns>
81
    public int Length
82
    {
83
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
84
        get => _inner.Length;
1,356✔
85
    }
86

87
    /// <summary>
88
    /// Indicates whether the current <see cref="U8String"/> is empty.
89
    /// </summary>
90
    /// <returns><see langword="true"/> if the current <see cref="U8String"/> is empty; otherwise, <see langword="false"/>.</returns>
91
    [MemberNotNullWhen(false, nameof(_value))]
92
    public bool IsEmpty
93
    {
94
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
95
        get => _value is null;
730✔
96
        //get => Length is 0; -> regresses Warpskimmer benchmarks
97
    }
98

99
    /// <inheritdoc/>
100
    int ICollection<byte>.Count => Length;
×
101

102
    /// <inheritdoc/>
103
    bool ICollection<byte>.IsReadOnly => true;
×
104

105
    /// <summary>
106
    /// Similar to <see cref="UnsafeRef"/>, but does not throw NRE if <see cref="IsEmpty"/> is true.
107
    /// </summary>
108
    /// <remarks>
109
    /// cmov's the ref out of byte[] if it is not null and uncoditionally increments it by <see cref="Offset"/>.
110
    /// </remarks>
111
    internal ref byte DangerousRef
112
    {
113
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
114
        get
115
        {
116
            var value = _value;
×
117
            ref var reference = ref Unsafe.NullRef<byte>();
×
118
            if (value != null) reference = ref MemoryMarshal.GetArrayDataReference(value);
×
119
            reference = ref Unsafe.Add(ref reference, Offset);
×
120
            return ref reference;
×
121
        }
122
    }
123

124
    /// <summary>
125
    /// Will throw NRE if <see cref="IsEmpty"/> is true.
126
    /// </summary>
127
    internal ref byte UnsafeRef
128
    {
129
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
130
        get => ref Unsafe.Add(
35✔
131
            ref MemoryMarshal.GetArrayDataReference(_value!), (nint)(uint)Offset);
35✔
132
    }
133

134
    /// <summary>
135
    /// Will throw NRE if <see cref="IsEmpty"/> is true.
136
    /// </summary>
137
    internal ReadOnlySpan<byte> UnsafeSpan
138
    {
139
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
140
        get => MemoryMarshal.CreateReadOnlySpan(ref UnsafeRef, Length);
35✔
141
    }
142

143
    /// <summary>
144
    /// Will throw NRE if <see cref="IsEmpty"/> is true.
145
    /// </summary>
146
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
147
    internal ref byte UnsafeRefAdd(int index)
148
    {
149
        return ref Unsafe.Add(
×
150
            ref MemoryMarshal.GetArrayDataReference(_value!), (nint)(uint)Offset + (nint)(uint)index);
×
151
    }
152

153
    /// <summary>
154
    /// Evaluates if the current <see cref="U8String"/> contains only ASCII characters.
155
    /// </summary>
156
    public bool IsAscii() => Ascii.IsValid(this);
×
157

158
    /// <summary>
159
    /// Evaluates if the current <see cref="U8String"/> is normalized to the specified
160
    /// Unicode normalization form (default: <see cref="NormalizationForm.FormC"/>).
161
    /// </summary>
162
    public bool IsNormalized(NormalizationForm form = NormalizationForm.FormC) => throw new NotImplementedException();
×
163

164
    /// <summary>
165
    /// Validates that the <paramref name="value"/> is a valid UTF-8 byte sequence.
166
    /// </summary>
167
    /// <param name="value">The <see cref="ReadOnlySpan{T}"/> to validate.</param>
168
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
169
    public static bool IsValid(ReadOnlySpan<byte> value) => Utf8.IsValid(value);
40✔
170

171
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
172
    internal static void Validate(ReadOnlySpan<byte> value)
173
    {
174
        if (!IsValid(value))
40!
175
        {
176
            ThrowHelpers.InvalidUtf8();
×
177
        }
178
    }
40✔
179

180
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
181
    internal static bool ValidateSlice(ReadOnlySpan<byte> value, int offset, int length)
182
    {
183
        // TODO: Another method which requires like 10 iterations to achieve good codegen.
184
        throw new NotImplementedException();
×
185
    }
186

187
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
188
    internal void Deconstruct(out byte[]? value, out int offset, out int length)
189
    {
190
        value = _value;
642✔
191
        offset = Offset;
642✔
192
        length = Length;
642✔
193
    }
642✔
194

195
    [EditorBrowsable(EditorBrowsableState.Never)]
196
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
197
    public ref readonly byte GetPinnableReference() => ref DangerousRef;
×
198

199
    void IList<byte>.Insert(int index, byte item) => throw new NotImplementedException();
×
200
    void IList<byte>.RemoveAt(int index) => throw new NotImplementedException();
×
201
    void ICollection<byte>.Add(byte item) => throw new NotImplementedException();
×
202
    void ICollection<byte>.Clear() => throw new NotImplementedException();
×
203
    bool ICollection<byte>.Remove(byte item) => throw new NotImplementedException();
×
204
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc