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

neon-sunset / U8String / 5982345353

26 Aug 2023 02:33AM UTC coverage: 18.315% (-1.4%) from 19.692%
5982345353

push

github

neon-sunset
feat: implement U8RefSplit<TComparer>, extend U8Searching specializations for codegen quality

134 of 1042 branches covered (0.0%)

Branch coverage included in aggregate %.

187 of 187 new or added lines in 4 files covered. (100.0%)

479 of 2305 relevant lines covered (20.78%)

35718.98 hits per line

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

0.0
/src/U8String.Splitting.cs
1
using System.Text;
2

3
using U8Primitives.Abstractions;
4

5
#pragma warning disable RCS1085, RCS1085FadeOut, IDE0032 // Use auto-implemented property. Why: readable fields.
6
namespace U8Primitives;
7

8
public readonly partial struct U8String
9
{
10
    public U8SplitPair SplitFirst(byte separator)
11
    {
12
        if (!U8Info.IsAsciiByte(separator))
×
13
        {
14
            // TODO: EH UX
15
            ThrowHelpers.ArgumentOutOfRange();
×
16
        }
17

18
        var source = this;
×
19
        if (!source.IsEmpty)
×
20
        {
21
            var span = source.UnsafeSpan;
×
22
            var index = span.IndexOf(separator);
×
23
            if (index >= 0)
×
24
            {
25
                return new(source, index, 1);
×
26
            }
27

28
            return U8SplitPair.NotFound(source);
×
29
        }
30

31
        return default;
×
32
    }
33

34
    public U8SplitPair SplitFirst(char separator) => char.IsAscii(separator)
×
35
        ? SplitFirst((byte)separator)
×
36
        : SplitFirstUnchecked(U8Scalar.Create(separator, checkAscii: false).AsSpan());
×
37

38
    public U8SplitPair SplitFirst(Rune separator) => separator.IsAscii
×
39
        ? SplitFirst((byte)separator.Value)
×
40
        : SplitFirstUnchecked(U8Scalar.Create(separator, checkAscii: false).AsSpan());
×
41

42
    public U8SplitPair SplitFirst(U8String separator)
43
    {
44
        var source = this;
×
45
        if (!source.IsEmpty)
×
46
        {
47
            if (!separator.IsEmpty)
×
48
            {
49
                var span = source.UnsafeSpan;
×
50
                var index = span.IndexOf(separator.UnsafeSpan);
×
51
                if (index >= 0)
×
52
                {
53
                    return new(source, index, separator.Length);
×
54
                }
55
            }
56

57
            return U8SplitPair.NotFound(source);
×
58
        }
59

60
        return default;
×
61
    }
62

63
    // It would be *really nice* to aggressively inline this method
64
    // but the way validation is currently implemented does not significantly
65
    // benefit from splitting on UTF-8 literals while possibly risking
66
    // running out of inlining budget significantly regressing performance everywhere else.
67
    public U8SplitPair SplitFirst(ReadOnlySpan<byte> separator)
68
    {
69
        var source = this;
×
70
        if (!source.IsEmpty)
×
71
        {
72
            if (!separator.IsEmpty)
×
73
            {
74
                var span = source.UnsafeSpan;
×
75
                var index = span.IndexOf(separator);
×
76
                if (index >= 0)
×
77
                {
78
                    // Same as with Slice(int, int), this might dereference past the end of the string.
79
                    // TODO: Do something about it if it's ever an issue.
80
                    if (U8Info.IsContinuationByte(source.UnsafeRefAdd(index)) ||
×
81
                        U8Info.IsContinuationByte(source.UnsafeRefAdd(index + separator.Length)))
×
82
                    {
83
                        ThrowHelpers.InvalidSplit();
×
84
                    }
85

86
                    return new(source, index, separator.Length);
×
87
                }
88
            }
89

90
            return U8SplitPair.NotFound(source);
×
91
        }
92

93
        return default;
×
94
    }
95

96
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
97
    public U8SplitPair SplitFirstUnchecked(ReadOnlySpan<byte> separator)
98
    {
99
        var source = this;
×
100
        if (!source.IsEmpty)
×
101
        {
102
            if (!separator.IsEmpty)
×
103
            {
104
                var span = source.UnsafeSpan;
×
105
                var index = span.IndexOf(separator);
×
106
                if (index >= 0)
×
107
                {
108
                    return new(source, index, separator.Length);
×
109
                }
110
            }
111

112
            return U8SplitPair.NotFound(source);
×
113
        }
114

115
        return default;
×
116
    }
117

118
    public U8SplitPair SplitLast(byte separator)
119
    {
120
        if (!U8Info.IsAsciiByte(separator))
×
121
        {
122
            // TODO: EH UX
123
            ThrowHelpers.ArgumentOutOfRange();
×
124
        }
125

126
        var source = this;
×
127
        if (!source.IsEmpty)
×
128
        {
129
            var span = source.UnsafeSpan;
×
130
            var index = span.LastIndexOf(separator);
×
131
            if (index >= 0)
×
132
            {
133
                return new(source, index, 1);
×
134
            }
135

136
            return U8SplitPair.NotFound(source);
×
137
        }
138

139
        return default;
×
140
    }
141

142
    public U8SplitPair SplitLast(char separator) => char.IsAscii(separator)
×
143
        ? SplitLast((byte)separator)
×
144
        : SplitLastUnchecked(U8Scalar.Create(separator, checkAscii: false).AsSpan());
×
145

146
    public U8SplitPair SplitLast(Rune separator) => separator.IsAscii
×
147
        ? SplitLast((byte)separator.Value)
×
148
        : SplitLastUnchecked(U8Scalar.Create(separator, checkAscii: false).AsSpan());
×
149

150
    public U8SplitPair SplitLast(U8String separator)
151
    {
152
        var source = this;
×
153
        if (!source.IsEmpty)
×
154
        {
155
            if (!separator.IsEmpty)
×
156
            {
157
                var span = source.UnsafeSpan;
×
158
                var index = span.LastIndexOf(separator.UnsafeSpan);
×
159
                if (index >= 0)
×
160
                {
161
                    return new(source, index, separator.Length);
×
162
                }
163
            }
164

165
            return U8SplitPair.NotFound(source);
×
166
        }
167

168
        return default;
×
169
    }
170

171
    public U8SplitPair SplitLast(ReadOnlySpan<byte> separator)
172
    {
173
        var source = this;
×
174
        if (!source.IsEmpty)
×
175
        {
176
            if (!separator.IsEmpty)
×
177
            {
178
                var span = source.UnsafeSpan;
×
179
                var index = span.LastIndexOf(separator);
×
180
                if (index >= 0)
×
181
                {
182
                    if (U8Info.IsContinuationByte(source.UnsafeRefAdd(index)) ||
×
183
                        U8Info.IsContinuationByte(source.UnsafeRefAdd(index + separator.Length)))
×
184
                    {
185
                        ThrowHelpers.InvalidSplit();
×
186
                    }
187

188
                    return new(source, index, separator.Length);
×
189
                }
190
            }
191

192
            return U8SplitPair.NotFound(source);
×
193
        }
194

195
        return default;
×
196
    }
197

198
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
199
    public U8SplitPair SplitLastUnchecked(ReadOnlySpan<byte> separator)
200
    {
201
        var source = this;
×
202
        if (!source.IsEmpty)
×
203
        {
204
            if (!separator.IsEmpty)
×
205
            {
206
                var span = source.UnsafeSpan;
×
207
                var index = span.LastIndexOf(separator);
×
208
                if (index >= 0)
×
209
                {
210
                    return new(source, index, separator.Length);
×
211
                }
212
            }
213

214
            return U8SplitPair.NotFound(source);
×
215
        }
216

217
        return default;
×
218
    }
219

220
    public U8Split<byte> Split(byte separator)
221
    {
222
        if (!U8Info.IsAsciiByte(separator))
×
223
        {
224
            ThrowHelpers.ArgumentOutOfRange();
×
225
        }
226

227
        return new(this, separator);
×
228
    }
229

230
    public U8Split<char> Split(char separator)
231
    {
232
        if (char.IsSurrogate(separator))
×
233
        {
234
            ThrowHelpers.ArgumentOutOfRange();
×
235
        }
236

237
        return new(this, separator);
×
238
    }
239

240
    public U8Split<Rune> Split(Rune separator) => new(this, separator);
×
241

242
    public U8Split Split(U8String separator)
243
    {
244
        return new(this, separator);
×
245
    }
246

247
    public U8RefSplit Split(ReadOnlySpan<byte> separator)
248
    {
249
        if (!IsValid(separator))
×
250
        {
251
            ThrowHelpers.InvalidSplit();
×
252
        }
253

254
        return new(this, separator);
×
255
    }
256

257
    public ConfiguredU8Split<byte> Split(byte separator, U8SplitOptions options)
258
    {
259
        if (!U8Info.IsAsciiByte(separator))
×
260
        {
261
            ThrowHelpers.ArgumentOutOfRange();
×
262
        }
263

264
        return new(this, separator, options);
×
265
    }
266

267
    public ConfiguredU8Split<char> Split(char separator, U8SplitOptions options)
268
    {
269
        if (char.IsSurrogate(separator))
×
270
        {
271
            ThrowHelpers.ArgumentOutOfRange();
×
272
        }
273

274
        return new(this, separator, options);
×
275
    }
276

277
    // TODO: Consider aggregating multiple interfaces into a single IU8Searcher (better name???) interface.
278
    public U8Split<byte, T> Split<T>(byte separator, T comparer)
279
        where T : IU8ContainsOperator, IU8CountOperator, IU8IndexOfOperator
280
    {
281
        if (!U8Info.IsAsciiByte(separator))
×
282
        {
283
            ThrowHelpers.ArgumentOutOfRange();
×
284
        }
285

286
        return new(this, separator, comparer);
×
287
    }
288

289
    public U8Split<char, T> Split<T>(char separator, T comparer)
290
        where T : IU8ContainsOperator, IU8CountOperator, IU8IndexOfOperator
291
    {
292
        if (char.IsSurrogate(separator))
×
293
        {
294
            ThrowHelpers.ArgumentOutOfRange();
×
295
        }
296

297
        return new(this, separator, comparer);
×
298
    }
299

300
    public U8Split<Rune, T> Split<T>(Rune separator, T comparer)
301
        where T : IU8ContainsOperator, IU8CountOperator, IU8IndexOfOperator
302
    {
303
        return new(this, separator, comparer);
×
304
    }
305

306
    public U8Split<U8String, T> Split<T>(U8String separator, T comparer)
307
        where T : IU8ContainsOperator, IU8CountOperator, IU8IndexOfOperator
308
    {
309
        return new(this, separator, comparer);
×
310
    }
311

312
    public U8RefSplit<T> Split<T>(ReadOnlySpan<byte> separator, T comparer)
313
        where T : IU8ContainsOperator, IU8CountOperator, IU8IndexOfOperator
314
    {
315
        if (!IsValid(separator))
×
316
        {
317
            ThrowHelpers.InvalidSplit();
×
318
        }
319

320
        return new(this, separator, comparer);
×
321
    }
322

323
    public ConfiguredU8Split<Rune> Split(Rune separator, U8SplitOptions options) => new(this, separator, options);
×
324

325
    public ConfiguredU8Split Split(U8String separator, U8SplitOptions options)
326
    {
327
        return new(this, separator, options);
×
328
    }
329
}
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