Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions BitsKit.Generator/Models/BitFieldModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,8 @@ private bool TryGetByteStorageSource(bool writable, out string source)
source = BackingFieldType switch
{
BackingFieldType.Memory => "{4}.Span",
BackingFieldType.Span when BackingField.TypeString == "byte[]" =>
writable ? "((Span<Byte>){4})" : "((ReadOnlySpan<Byte>){4})",
BackingFieldType.Span => "{4}",
BackingFieldType.InlineArray when BackingField.TypeString == "byte" =>
writable ? "((Span<Byte>)this)" : "((ReadOnlySpan<Byte>)this)",
Expand Down
16 changes: 16 additions & 0 deletions BitsKit.Tests/GeneratorTests.Models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,22 @@ public partial struct SpecializedMemoryAccessorStruct
public Memory<byte> BigEndianBacking48;
}

[BitObject(BitOrder.LeastSignificant)]
public partial struct OffsetArrayAccessorStruct
{
[BitField(8)]
[BitField("AlignedValue", 32, BitFieldType.UInt32)]
public byte[] AlignedBacking;

[BitField(13)]
[BitField("Value11", 11, BitFieldType.UInt32)]
public byte[] Backing11;

[BitField(15)]
[BitField("BigEndianValue48", 48, BitFieldType.UInt64, ReverseBitOrder = true)]
public byte[] BigEndianBacking48;
}

[BitObject(BitOrder.LeastSignificant, AccessMode = BitObjectAccessMode.Unsafe)]
public partial struct UnsafeMemoryAccessorStruct
{
Expand Down
45 changes: 45 additions & 0 deletions BitsKit.Tests/GeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,51 @@ public void SpecializedStorageAccessorsMatchReferenceBits()
}
}

[TestMethod]
public void OffsetArrayAccessorsMatchReferenceBits()
{
Random random = new(0xA22A7);

for (int i = 0; i < 1000; i++)
{
byte[] alignedBytes = new byte[5];
byte[] bytes11 = new byte[(i & 1) == 0 ? 3 : 5];
byte[] bigEndianBytes48 = new byte[(i & 1) == 0 ? 8 : 9];
random.NextBytes(alignedBytes);
random.NextBytes(bytes11);
random.NextBytes(bigEndianBytes48);

var actual = new OffsetArrayAccessorStruct
{
AlignedBacking = alignedBytes,
Backing11 = bytes11,
BigEndianBacking48 = bigEndianBytes48
};

Assert.AreEqual(BitPrimitives.ReadUInt32LSB(alignedBytes, 8, 32), actual.AlignedValue);
Assert.AreEqual((uint)Helpers.ReadBitsLSB(bytes11, 13, 11), actual.Value11);
Assert.AreEqual(Helpers.ReadBitsMSB(bigEndianBytes48, 15, 48), actual.BigEndianValue48);

uint nextAligned = unchecked((uint)random.NextInt64());
uint next11 = (uint)random.Next(1 << 11);
ulong next48 = unchecked((ulong)random.NextInt64()) & 0xFFFFFFFFFFFFUL;
byte[] expectedAligned = (byte[])alignedBytes.Clone();
byte[] expected11 = (byte[])bytes11.Clone();
byte[] expected48 = (byte[])bigEndianBytes48.Clone();
BitPrimitives.WriteUInt32LSB(expectedAligned, 8, nextAligned, 32);
Helpers.WriteBitsLSB(expected11, 13, next11, 11);
Helpers.WriteBitsMSB(expected48, 15, next48, 48);

actual.AlignedValue = nextAligned;
actual.Value11 = next11;
actual.BigEndianValue48 = next48;

CollectionAssert.AreEqual(expectedAligned, alignedBytes);
CollectionAssert.AreEqual(expected11, bytes11);
CollectionAssert.AreEqual(expected48, bigEndianBytes48);
}
}

[TestMethod]
public void ReadOnlyMemberTest()
{
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ Notable changes to the community-maintained fork are documented here. This proje
- `BitBatchPrimitives` provides allocation-free contiguous and record-strided reads and writes for every supported integral type, Boolean values, and both bit orders.
- `BitObjectAttribute.GenerateBatchAccessors` generates strongly typed packed and strided helpers for integral, Boolean, and enum fields.

### Fixed

- Array-backed generated accessors compile when an optimized field begins at a nonzero byte offset.

## 1.5.0 - 2026-07-21

### Added
Expand Down
Loading