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

neon-sunset / U8String / 5922049812

21 Aug 2023 04:39AM UTC coverage: 20.136% (-1.6%) from 21.736%
5922049812

push

github

neon-sunset
feat: work in progress - comparers/globalization, planning, splitters and native string prototype scaffolding

122 of 856 branches covered (14.25%)

Branch coverage included in aggregate %.

188 of 188 new or added lines in 9 files covered. (100.0%)

439 of 1930 relevant lines covered (22.75%)

26474.14 hits per line

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

0.0
/src/Prototypes/NativeU8String.cs
1
using System.Text;
2

3
namespace U8Primitives; // .InteropServices?
4

5
// The best is yet to come :^)
6
// Q: store a bit in _length to indicate if it's null-terminated?
7
// Is it viable to do some tagged pointer shenanigans?
8
// Q: Consider some clever WeakReference handler with dealloc callback? What kind of object should I use for root?
9
// Q: UnmanagedU8String? How does CString look like?
10
// Q: NativeU8String<TAlloc>?
11
// Q: Always null-terminated?
12
// TODO: Double-check to ensure zero-extension on all casts and math (after all, _length must never be negative)
13
// TODO: Sanity of handling free(_ptr) seems to necessitate introducing U8Slice or U8Span after all...this is because
14
// _ptr must remain pointing to a start of the original allocation. Seriously consider Rust-like approach where
15
// 1) the main type is a string slice and 2) it can be implicitly converted to and implements the majority of the API surface,
16
// so worst case it is possible to just e.g. U8String[..].Split(...) or U8String.AsSpan/Slice().Split(...).
17
// Alternatively, this can be handled through generic extension methods but that would make it incompatible with ref structs.
18
// Another option: refactor abstractions into trait-like interfaces and have IUncheckedSliceable<TSelf>, ISpanConvertible<TElement>, etc.
19
#pragma warning disable IDE0032, RCS1085 // Use auto-implemented property. Why: readable layout
20
internal unsafe readonly struct NativeU8String
21
{
22
    readonly byte* _ptr;
23
    readonly nint _length;
24

25
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
26
    public NativeU8String(byte* ptr, nint length)
27
    {
28
        _ptr = ptr;
×
29
        _length = length;
×
30
    }
×
31

32
    public nint Length => _length;
×
33

34
    // TODO: Write an analyzer that warns against indexing with `int` which can overflow.
35
    public ref readonly byte this[nint index]
36
    {
37
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
38
        get
39
        {
40
            if ((nuint)index >= (nuint)_length)
×
41
            {
42
                ThrowHelpers.IndexOutOfRange();
×
43
            }
44

45
            return ref _ptr[index];
×
46
        }
47
    }
48

49
    public NativeU8String this[Range range]
50
    {
51
        get
52
        {
53
            var source = this;
×
54
            var (start, length) = range.GetOffsetAndLength((int)source._length);
×
55

56
            if ((start > 0 && U8Info.IsContinuationByte(in source._ptr[start])) || (
×
57
                length < source.Length && U8Info.IsContinuationByte(in source._ptr[start + length])))
×
58
            {
59
                // TODO: Exception message UX
60
                ThrowHelpers.InvalidSplit();
×
61
            }
62

63
            return new(_ptr + start, length);
×
64
        }
65
    }
66

67
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
68
    public ReadOnlySpan<byte> AsSpan()
69
    {
70
        return new(_ptr, (int)_length);
×
71
    }
72

73
    public NativeU8String Slice(nint start)
74
    {
75
        var source = this;
×
76
        // From ReadOnly/Span<T> Slice(int) implementation
77
        if ((nuint)start > (nuint)source.Length)
×
78
        {
79
            ThrowHelpers.ArgumentOutOfRange();
×
80
        }
81

82
        var length = source.Length - start;
×
83
        if (length > 0)
×
84
        {
85
            if (U8Info.IsContinuationByte(in source._ptr[start]))
×
86
            {
87
                ThrowHelpers.InvalidSplit();
×
88
            }
89

90
            return new(source._ptr + start, length);
×
91
        }
92

93
        return default;
×
94
    }
95

96
    public NativeU8String Slice(nint start, nint length)
97
    {
98
        var source = this;
×
99
        // From ReadOnly/Span<T> Slice(int, int) implementation
100
        if ((nuint)start + (nuint)length > (nuint)source.Length)
×
101
        {
102
            ThrowHelpers.ArgumentOutOfRange();
×
103
        }
104

105
        var result = default(NativeU8String);
×
106
        if (length > 0)
×
107
        {
108
            // TODO: If this is always null-terminated, should we skip length check?
109
            if ((start > 0 && U8Info.IsContinuationByte(in source._ptr[start])) || (
×
110
                length < source.Length && U8Info.IsContinuationByte(in source._ptr[start + length])))
×
111
            {
112
                // TODO: Exception message UX
113
                ThrowHelpers.InvalidSplit();
×
114
            }
115

116
            result = new(_ptr + start, length);
×
117
        }
118

119
        return result;
×
120
    }
121

122
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
123
    public static implicit operator ReadOnlySpan<byte>(NativeU8String str) => str.AsSpan();
×
124

125
    public override string ToString()
126
    {
127
        return Encoding.UTF8.GetString(this);
×
128
    }
129
}
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