-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathBitFieldAttribute.cs
More file actions
170 lines (159 loc) · 7.44 KB
/
Copy pathBitFieldAttribute.cs
File metadata and controls
170 lines (159 loc) · 7.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
namespace Stardust.Utilities;
/// <summary>
/// Marks a property as a multi-bit field within a [BitFields] struct.
/// </summary>
/// <remarks>
/// <para>
/// Specify the bit range using any two of the three values <see cref="Start"/>,
/// <see cref="End"/> (inclusive end position), and <see cref="Width"/> (bit count).
/// The third value is derived automatically: <c>Width = End - Start + 1</c>,
/// <c>End = Start + Width - 1</c>, or <c>Start = End - Width + 1</c>.
/// </para>
/// <para>
/// The property must be declared as <c>public partial {type}</c> where type is
/// any supported numeric type (byte, sbyte, ushort, short, uint, int, ulong, long,
/// nint, nuint, Half, float, double, decimal, UInt128, Int128), an enum, or an
/// embedded <c>[BitFields]</c> struct. The generator will implement the getter/setter
/// using inline bit manipulation.
/// </para>
/// <example>
/// <code>
/// [BitFields(typeof(byte))]
/// public partial struct RegisterA
/// {
/// // Width syntax (preferred):
/// [BitField(0, Width = 3)] public partial byte Sound { get; set; }
/// [BitField(3, Width = 1)] public partial byte Flag { get; set; }
/// [BitField(4, Width = 4)] public partial byte Mode { get; set; }
///
/// // End syntax (inclusive):
/// // [BitField(0, End = 2)] public partial byte Sound { get; set; }
///
/// // End + Width derives Start (Start = End - Width + 1):
/// // [BitField(End = 2, Width = 3)] public partial byte Sound { get; set; }
///
/// // Second parameter is 'end' (inclusive).
/// // A warning message is generated to clarify that this syntax is potentially confusing and can be mistaken for 'Width':
/// // [BitField(0, 2)] public partial byte Sound { get; set; }
///
/// // Fully named syntax:
/// // [BitField(Start = 0, Width = 3)] public partial byte Sound { get; set; }
/// // [BitField(Start = 0, End = 2)] public partial byte Sound { get; set; }
/// }
/// </code>
/// </example>
/// </remarks>
[AttributeUsage(AttributeTargets.Property)]
public sealed class BitFieldAttribute : Attribute
{
/// <summary>
/// The starting bit position (0-based, inclusive).
/// </summary>
public int Start { get; set; } = -1;
/// <summary>
/// The ending bit position (0-based, inclusive).
/// Mutually exclusive with <see cref="Width"/>; specify one or the other.
/// </summary>
public int End { get; set; } = -1;
/// <summary>
/// The width of the field in bits. For example, <c>Width = 3</c> defines a 3-bit field.
/// Mutually exclusive with <see cref="End"/>; specify one or the other.
/// </summary>
public int Width { get; set; } = -1;
/// <summary>
/// Normally set to <see cref="MustBe.Any"/> and has no effect.
/// When set to <see cref="MustBe.Zero"/> or <see cref="MustBe.One"/>, it overrides the behavior of the field's
/// bits on write and during conversion of the underlying BitFields struct to/from other types.
/// </summary>
public MustBe ValueOverride { get; set; } = MustBe.Any;
/// <summary>
/// An optional human-readable description of this field.
/// When <see cref="DescriptionResourceType"/> is null, this is a literal string.
/// When <see cref="DescriptionResourceType"/> is set, this is a resource key
/// resolved at runtime via the resource type's <c>ResourceManager</c>.
/// </summary>
/// <example>
/// <code>
/// // Literal description (no localization)
/// [BitField(0, Width = 4, Description = "IP header version")]
/// public partial byte Version { get; set; }
///
/// // Localized description via .resx resource file
/// [BitField(0, Width = 4, Description = nameof(Strings.IpVersion),
/// DescriptionResourceType = typeof(Strings))]
/// public partial byte Version { get; set; }
/// </code>
/// </example>
public string? Description { get; set; }
/// <summary>
/// When set, <see cref="Description"/> is treated as a resource key and resolved
/// at runtime via this type's <c>ResourceManager</c> property.
/// The type must have a static <c>ResourceManager</c> property (as generated by .resx files).
/// </summary>
public Type? DescriptionResourceType { get; set; }
/// <summary>
/// When <c>true</c>, the generated property setter clamps the incoming value to the
/// valid range for this field's bit width instead of silently truncating (wrapping).
/// For unsigned property types the value is clamped to <c>[0, 2^Width - 1]</c>.
/// For signed property types it is clamped to <c>[-(2^(Width-1)), 2^(Width-1) - 1]</c>.
/// Defaults to <c>false</c> (truncating behaviour).
/// </summary>
/// <remarks>
/// Supported property types: <c>byte</c>, <c>sbyte</c>, <c>ushort</c>, <c>short</c>,
/// <c>uint</c>, <c>int</c>, <c>ulong</c>, <c>long</c>, <c>nint</c>, <c>nuint</c>.
/// Saturation is silently ignored for floating-point types (<c>Half</c>, <c>float</c>,
/// <c>double</c>, <c>decimal</c>), embedded <c>[BitFields]</c> struct types, enum types,
/// and fields whose <see cref="ValueOverride"/> forces a fixed value.
/// Saturation is also a no-op when the field width equals the full property type width.
/// The <c>With{Name}</c> fluent method is similarly clamped when <c>Saturating</c> is true.
/// </remarks>
public bool Saturating { get; set; } = false;
/// <summary>
/// Creates a new bit field attribute with all properties specified via named syntax.
/// </summary>
/// <example>
/// <code>
/// [BitField(Start = 0, Width = 3)]
/// [BitField(Start = 0, End = 2)]
/// [BitField(End = 2, Width = 3)] // Start derived as End - Width + 1 = 0
/// </code>
/// </example>
public BitFieldAttribute() { }
/// <summary>
/// Creates a new bit field attribute with the starting bit position.
/// Specify the field range using <see cref="End"/> or <see cref="Width"/>.
/// </summary>
/// <param name="start">The starting bit position (0-based, inclusive).</param>
/// <param name="mustBe">Optional override for how bits are handled. Defaults to <see cref="MustBe.Any"/>.</param>
/// <example>
/// <code>
/// [BitField(0, Width = 3)]
/// [BitField(0, End = 2)]
/// [BitField(3, MustBe.Zero, Width = 1)]
/// </code>
/// </example>
public BitFieldAttribute(int start, MustBe mustBe = MustBe.Any)
{
Start = start;
ValueOverride = mustBe;
}
/// <summary>
/// Creates a new bit field attribute with the starting and ending bit positions.
/// Second parameter is 'end', not to be confused with 'Width'.
/// Use named <see cref="End"/> or <see cref="Width"/> properties instead:
/// <c>[BitField(start, End = N)]</c> or <c>[BitField(start, Width = N)]</c>.
/// If <paramref name="end"/> is less than <paramref name="start"/>, the two values
/// are silently swapped so either order is accepted.
/// </summary>
/// <param name="start">The starting bit position (0-based, inclusive).</param>
/// <param name="end">The end bit position (0-based, inclusive). If less than <paramref name="start"/>, the values are swapped automatically.</param>
/// <param name="mustBe">Optional override for how bits are handled for this field.</param>
public BitFieldAttribute(int start, int end, MustBe mustBe = MustBe.Any)
{
if (end < start)
(start, end) = (end, start);
Start = start;
End = end;
ValueOverride = mustBe;
}
}