diff --git a/BeefLibs/corlib/src/Bitfield.bf b/BeefLibs/corlib/src/Bitfield.bf new file mode 100644 index 00000000..319db448 --- /dev/null +++ b/BeefLibs/corlib/src/Bitfield.bf @@ -0,0 +1,279 @@ +using System.Reflection; +using System.Diagnostics; + +namespace System +{ + [AttributeUsage(.Field | .StaticField)] + struct BitfieldAttribute : Attribute, IOnFieldInit + { + public enum BitSpec + { + case Auto; + case AutoAt(int pos); + case AutoRev; + case Bits(int bits); + case BitsRev(int bits); + case BitsAt(int bits, int pos); + case BitRange(IndexRange range); + case ValueRange(Range range); + case ValueRangeRev(Range range); + case ValueRangeAt(Range range, int pos); + } + + public enum ProtectionKind + { + Private = 0, + Public = 1, + Protected = 2, + } + + public enum AccessFlags + { + Read = 1, + Write = 2 + } + + protected String mName; + protected int32 mBitPos = -1; + protected BitSpec mBitSpec; + protected int32 mFieldIdx = 0; + protected Type mBFieldType = null; + protected ProtectionKind mProtection; + protected AccessFlags mAccessFlags; + + public this(ProtectionKind protection, BitSpec bitSpec, String name, AccessFlags accessFlags = .Read | .Write) + { + mName = name; + mBitSpec = bitSpec; + mProtection = protection; + mAccessFlags = accessFlags; + } + + [Comptime] + public void OnFieldInit(ComptimeFieldInfo fieldInfo, Self* prev) mut + { + if (mBFieldType == null) + mBFieldType = fieldInfo.FieldType; + var buType = mBFieldType; + if (buType.IsEnum) + buType = buType.UnderlyingType; + + if (buType.IsFloatingPoint) + Runtime.FatalError("Invalid bitfield type"); + + bool isRev = false; + int32 bitCount = 0; + int32 underlyingBitCount = fieldInfo.FieldType.Size * 8; + + int32 GetBitSize(int min, int max) + { + var min; + var max; + if (min < 0) + min = ~min; + if (max < 0) + max = ~max; + uint64 value = (uint64)min | (uint64)max; + + int32 bitCount = 1; + if (buType.IsSigned) + bitCount++; + + while ((value >>= 1) != 0) + bitCount++; + + return bitCount; + } + + bool hasRange = false; + bool rangeCheckMin = false; + bool rangeCheckMax = false; + int minVal = 0; + int maxVal = 0; + + switch (mBitSpec) + { + case .Bits(let bits): + bitCount = (.)bits; + case .BitsRev(let bits): + bitCount = (.)bits; + isRev = true; + case .BitsAt(let bits, let pos): + bitCount = (.)bits; + mBitPos = (.)pos; + case .BitRange(let range): + bitCount = (.)range.GetLength(underlyingBitCount); + mBitPos = (.)range.Start.Get(underlyingBitCount); + case .ValueRange(let range): + minVal = range.Start; + maxVal = range.End - 1; + hasRange = true; + bitCount = GetBitSize(minVal, maxVal); + case .ValueRangeRev(let range): + minVal = range.Start; + maxVal = range.End - 1; + hasRange = true; + bitCount = GetBitSize(minVal, maxVal); + isRev = true; + case .ValueRangeAt(let range, int pos): + bitCount = GetBitSize(range.Start, range.End - 1); + bitCount = (.)pos; + default: + Runtime.FatalError("Invalid bitspec"); + } + + if ((prev != null) && (prev.mFieldIdx == fieldInfo.FieldIdx)) + { + if (mBitPos == -1) + { + mBitPos = prev.mBitPos; + if (isRev) + mBitPos -= bitCount; + } + } + + if (mBitPos == -1) + mBitPos = isRev ? underlyingBitCount - bitCount : 0; + + if (Compiler.IsBuilding) + Debug.WriteLine($"OnFieldInit Name:{mName} BitPos:{mBitPos} BitCount:{bitCount} BuType:{buType}"); + + int bitSize = fieldInfo.FieldType.Size * 8; + if ((mBitPos < 0) || (mBitPos + bitCount > bitSize)) + Runtime.FatalError("Bitfield exceeds bounds of underlying value"); + + String str = scope .(); + if (mProtection == .Public) + str.Append("public "); + else if (mProtection == .Protected) + str.Append("protected "); + + void TypeStr(String str, Type t) + { + if (t.IsPrimitive) + t.GetFullName(str); + else + str.AppendF($"comptype({(int)t.TypeId})"); + } + + String uTypeStr = TypeStr(.. scope .(), fieldInfo.FieldType); + String bTypeStr = TypeStr(.. scope .(), mBFieldType); + int mask = ((1 << mBitPos) << bitCount) - (1 << mBitPos); + + if ((!hasRange) && (mBFieldType.IsSigned)) + { + minVal = -(1<<(bitCount-1)); + maxVal = (1<<(bitCount-1))-1; + } + else if (!hasRange) + { + maxVal = (1<<(bitCount))-1; + } + + str.AppendF($"{bTypeStr} {mName}\n{{\n"); + + if (mAccessFlags.HasFlag(.Read)) + { + str.Append("\t[Inline]\n\tget => "); + if (mBFieldType == typeof(bool)) + str.AppendF($"(({fieldInfo.Name} & {mask}) >> {mBitPos}) != 0;\n"); + else if (buType.IsSigned) + str.AppendF($"(.)((int)((({fieldInfo.Name} & 0x{mask:X}) >> {mBitPos}) ^ 0x{1 << (bitCount - 1):X}) - 0x{1 << (bitCount - 1):X});\n"); + else + str.AppendF($"(.)(({fieldInfo.Name} & 0x{mask:X}) >> {mBitPos});\n"); + } + + if (mBFieldType.IsInteger) + { + if (mBFieldType.IsSigned) + { + rangeCheckMin = minVal != -(1<<(mBFieldType.Size*8-1)); + rangeCheckMax = maxVal != (1<<(mBFieldType.Size*8-1))-1; + } + else + { + rangeCheckMin = minVal != 0; + rangeCheckMax = maxVal != (1<= {minVal}) && (value <= {maxVal}));\n"); + else if ((pass == 0) && (rangeCheckMin)) + str.AppendF($"\t\tRuntime.Assert(value >= {minVal});\n"); + else if ((pass == 0) && (rangeCheckMax)) + str.AppendF($"\t\tRuntime.Assert(value <= {maxVal});\n"); + } + + if (mBFieldType == typeof(bool)) + str.AppendF($"\t\t{fieldInfo.Name} = ({fieldInfo.Name} & ({uTypeStr})~0x{mask:X}) | (value ? 0x{mask:X} : 0);\n"); + else + str.AppendF($"\t\t{fieldInfo.Name} = ({fieldInfo.Name} & ({uTypeStr})~0x{mask:X}) | ((({uTypeStr})value << {mBitPos}) & ({uTypeStr})0x{mask:X});\n"); + + str.Append("\t}\n"); + } + } + + str.Append("}\n"); + + Compiler.EmitTypeBody(fieldInfo.Owner, str); + + if (!isRev) + mBitPos += bitCount; + mFieldIdx = (.)fieldInfo.FieldIdx; + } + } + + [AttributeUsage(.Field | .StaticField)] + struct BitfieldAttribute : BitfieldAttribute + { + int GetBitCount() + { + int val = typeof(TField).BitSize; + return val; + } + + BitSpec FixSpec(BitSpec spec) + { + switch (spec) + { + case .Auto: + return .Bits(typeof(TField).BitSize); + case .AutoRev(): + return .BitsRev(typeof(TField).BitSize); + case .AutoAt(let pos): + return .BitsAt(typeof(TField).BitSize, pos); + default: + return spec; + } + } + + public this(ProtectionKind protection, String name, AccessFlags accessFlags = .Read | .Write) : base(protection, FixSpec(.Auto), name, accessFlags) + { + /*if (Compiler.IsBuilding) + Debug.WriteLine($"typeof.BitSize:{typeof(TField).BitSize} mBitCount:{mBitCount}"); //*/ + mBFieldType = typeof(TField); + } + + public this(ProtectionKind protection, BitSpec bitSpec, String name, AccessFlags accessFlags = .Read | .Write) : base(protection, FixSpec(bitSpec), name, accessFlags) + { + mBFieldType = typeof(TField); + } + } +} diff --git a/BeefLibs/corlib/src/Collections/List.bf b/BeefLibs/corlib/src/Collections/List.bf index c440f74b..70a4e7fb 100644 --- a/BeefLibs/corlib/src/Collections/List.bf +++ b/BeefLibs/corlib/src/Collections/List.bf @@ -216,12 +216,7 @@ namespace System.Collections [Checked] get { - int idx; - switch (index) - { - case .FromFront(let offset): idx = offset; - case .FromEnd(let offset): idx = mSize - offset; - } + int idx = index.Get(mSize); Runtime.Assert((uint)idx < (uint)mSize); return ref mItems[idx]; } @@ -229,24 +224,13 @@ namespace System.Collections [Unchecked, Inline] get { - int idx; - switch (index) - { - case .FromFront(let offset): idx = offset; - case .FromEnd(let offset): idx = mSize - offset; - } - return ref mItems[idx]; + return ref mItems[index.Get(mSize)]; } [Checked] set { - int idx; - switch (index) - { - case .FromFront(let offset): idx = offset; - case .FromEnd(let offset): idx = mSize - offset; - } + int idx = index.Get(mSize); Runtime.Assert((uint)idx < (uint)mSize); mItems[idx] = value; #if VERSION_LIST @@ -257,13 +241,7 @@ namespace System.Collections [Unchecked, Inline] set { - int idx; - switch (index) - { - case .FromFront(let offset): idx = offset; - case .FromEnd(let offset): idx = mSize - offset; - } - mItems[idx] = value; + mItems[index.Get(mSize)] = value; #if VERSION_LIST mVersion++; #endif diff --git a/BeefLibs/corlib/src/Range.bf b/BeefLibs/corlib/src/Range.bf index dae1f2a1..7614d3c6 100644 --- a/BeefLibs/corlib/src/Range.bf +++ b/BeefLibs/corlib/src/Range.bf @@ -24,6 +24,16 @@ namespace System offset.ToString(strBuffer); } } + + [Inline] + public int Get(int size) + { + switch (this) + { + case .FromFront(let offset): return offset; + case .FromEnd(let offset): return size - offset; + } + } } struct Range : RangeExpression, IEnumerable @@ -310,6 +320,14 @@ namespace System strBuffer.Append("..<"); mEnd.ToString(strBuffer); } + + public int GetLength(int size) + { + int length = mEnd.Get(size) - mStart.Get(size); + if (mIsClosed) + length++; + return length; + } } struct ClosedRange : RangeExpression, IEnumerable diff --git a/BeefySysLib/BeefySysLib.vcxproj b/BeefySysLib/BeefySysLib.vcxproj index b84cb83f..5a38ca1f 100644 --- a/BeefySysLib/BeefySysLib.vcxproj +++ b/BeefySysLib/BeefySysLib.vcxproj @@ -2153,6 +2153,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\" + diff --git a/BeefySysLib/BeefySysLib.vcxproj.filters b/BeefySysLib/BeefySysLib.vcxproj.filters index 016806a0..c82a47af 100644 --- a/BeefySysLib/BeefySysLib.vcxproj.filters +++ b/BeefySysLib/BeefySysLib.vcxproj.filters @@ -1105,6 +1105,9 @@ src\util + + src\util + diff --git a/BeefySysLib/util/BitSet.h b/BeefySysLib/util/BitSet.h new file mode 100644 index 00000000..87160ec5 --- /dev/null +++ b/BeefySysLib/util/BitSet.h @@ -0,0 +1,61 @@ +#pragma once + +#include "BFPlatform.h" + +NS_BF_BEGIN; + +#define BF_BITSET_ELEM_SIZE (sizeof(uintptr)*8) + +class BitSet +{ +public: + uintptr* mBits; + int mNumInts; + +public: + BitSet() + { + mNumInts = 0; + mBits = NULL; + } + + BitSet(int numBits) + { + mNumInts = 0; + mBits = NULL; + this->Resize(numBits); + } + + ~BitSet() + { + delete this->mBits; + } + + void Resize(int numBits) + { + int numInts = (numBits + BF_BITSET_ELEM_SIZE - 1) / BF_BITSET_ELEM_SIZE; + if (numInts == mNumInts) + return; + this->mNumInts = numInts; + delete this->mBits; + this->mBits = new uintptr[numInts]; + memset(this->mBits, 0, numInts * sizeof(uintptr)); + } + + bool IsSet(int idx) + { + return (this->mBits[idx / BF_BITSET_ELEM_SIZE] & ((uintptr)1 << (idx % BF_BITSET_ELEM_SIZE))) != 0; + } + + void Set(int idx) + { + this->mBits[idx / BF_BITSET_ELEM_SIZE] |= ((uintptr)1 << (idx % BF_BITSET_ELEM_SIZE)); + } + + void Clear(int idx) + { + this->mBits[idx / BF_BITSET_ELEM_SIZE] &= ~((uintptr)1 << (idx % BF_BITSET_ELEM_SIZE)); + } +}; + +NS_BF_END; \ No newline at end of file diff --git a/IDEHelper/Tests/src/Bitfields.bf b/IDEHelper/Tests/src/Bitfields.bf new file mode 100644 index 00000000..c2d10121 --- /dev/null +++ b/IDEHelper/Tests/src/Bitfields.bf @@ -0,0 +1,50 @@ +using System; +namespace Tests +{ + class Bitfields + { + enum EnumA + { + A = -64, + B = 12, + C = 63 + } + + struct StructA + { + [Bitfield(.Public, "A")] + [Bitfield(.Public, .Bits(3), "B")] + [Bitfield(.Public, "C")] + [Bitfield(.Public, .ValueRange(0..<12), "D")] + public int32 mVal; + + [Bitfield(.Public, .AutoRev, "E")] + [Bitfield(.Public, .BitsRev(8), "F")] + [Bitfield(.Public, .ValueRangeRev(0..<256), "G")] + [Bitfield(.Public, .BitsAt(8, 0), "H")] + public int32 mVal2; + } + + [Test] + static void TestBasics() + { + Test.Assert(sizeof(StructA) == 8); + StructA sa = .(); + sa.A = 0x12; + sa.B = 7; + sa.C = .C; + sa.D = 9; + sa.E = 0x22; + sa.F = 0x33; + sa.G = 0x44; + sa.H = 0x55; + Test.Assert(sa.A == 0x12); + Test.Assert(sa.B == 7); + Test.Assert(sa.C == .C); + Test.Assert(sa.D == 9); + + Test.Assert(sa.mVal == 0x0025FF12); + Test.Assert(sa.mVal2 == 0x22334455); + } + } +} \ No newline at end of file