Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a bit mask for relevant bits over multiple bytes programmatically

Tags:

c#

bit

bitmask

I'm working with a very long binary string that contains "fields" that can be everything from 2 to 32 bits long. So in some cases a "field" can span over multiple uneven bytes.

I managed to read out all the data but now I'm working editing and switching out data. Here is a current working example on a field that is 12 bits and start on a whole byte:

public class CardSectionProperty
{
    public CardSectionProperty(CardDataContainer data, MapItem mapItem)
    {
        Data = data;
        Start = mapItem.Position;
        Length = mapItem.Length;
    }

    public CardDataContainer Data { get; } // A simple container for the binary string of the whole card.

    public int Start { get; }

    public int Length { get; }

    public void SetValue(long value)
    {
        var relevantBits= 0xFF0F; // <- I think this mask need to change depending on the field size and position in the binary string.
        var newValue = (int)value;
        var oldValue = (int)GetValue();
        var bitsToKeep = (~relevantBits) & oldValue;
        var newValueCorrected = Switch16BitEndianness(newValue) | bitsToKeep;
        SetValue(Convert.ToString(newValueCorrected, 2));
    }

    private void SetValue(string binaryValue)
    {
        var builder = new StringBuilder(Data.BinaryData);
        builder.Remove(Start, binaryValue.Length);
        builder.Insert(Start, binaryValue);
        Data.BinaryData = builder.ToString();
    }

    private int Switch16BitEndianness(int x)
    {
        return ((x & 0xFF00) >> 8) | ((x & 0x00FF) << 8);
    }
}

Here the mask is 0xFF0F (1111 1111 0000 1111) which is easy to understand as we care about 12 bits. But what would be the easiest way to create this mask programmatically? As I said it can be pretty random and sometimes we can span over three bytes (using 2 bits from first, then 8, and then 2 again from the last etc).

Or is there a better way to do this?

Edit: Added a bit more code to make the example easier to understand.

like image 732
MilleB Avatar asked Aug 27 '26 05:08

MilleB


1 Answers

The main question here is actually... what are those bit fields? It's not very clear from your question where your data would come from and what it means.

See, generally, these bits will each represent a specific Boolean to enable and disable, so they should each have a specific name, too. The normal way this is represented is by using a [Flags] enum. They allow you to build up values where each bit flag is a named option you enable. There are loads of these already used in the .Net framework.

Basically, a flags enum is an enum that is defined as containing bits that can be combined. You just assign a name to each of the bits you use and combine them with OR operations.

[Flags]
public enum BitMask
{
    Empty = 0x00,    // 0000 0000 0000 0000
    BitOne = 0x01,   // 0000 0000 0000 0001
    BitTwo = 0x02,   // 0000 0000 0000 0010
    BitThree = 0x04, // 0000 0000 0000 0100
    BitFour = 0x08,  // 0000 0000 0000 1000
    BitFive = 0x10,  // 0000 0000 0001 0000
    BitSix = 0x20,   // 0000 0000 0010 0000
    BitSeven = 0x40, // 0000 0000 0100 0000
    BitEight = 0x80, // 0000 0000 1000 0000
    // etc...
}

You can even put commonly-used presets like BitsFourAndFive = 0x18 in there, which is pretty handy. Of course, your actual options should have the names of the booleans they actually represent.

Then you can combine your bits simply with

BitMask mask = BitMask.BitTwo | BitMask.BitFive | BitMask.BitEight;

I assume you know these, since you use them in your code, but anyway... to remove a bit from the mask, use an AND operation on the inverted value:

mask &= ~BitMask.BitTwo;

...and to check whether a bit is in the mask:

Boolean isTwo = (mask & BitMask.BitTwo) != 0;

And finally, to get your actual value... simply cast it to integer.

Int32 maskvalue = (Int32)mask;

The same can obviously be done the other way around too; if you have a value read from your input, you can just cast it to BitMask. A possible "is bit two enabled" check after reading a value:

Int32 readValue = ReadValue(); // wherever this comes from
Boolean isTwo = ((BitMask)readValue & BitMask.BitTwo) != 0;

Alternately, you can cast the BitMask enum entry to Int32; that works too.

Here's a real example from one of my projects that was used by a bunch of game sprite classes to both expose what kind of image they contained, and what kind of images they could use as input for saving as that sprite type:

[Flags]
public enum FileClass
{
    None = 0x00,
    Image1Bit = 0x01,
    Image4Bit = 0x02,
    Image8Bit = 0x04,
    ImageIndexed = 0x07,
    ImageHiCol = 0x08,
    Image = 0x0F,
}

As you see, for the save input data, I put the quick presets Image and ImageIndexed in there that combine multiple bits, to quickly indicate that the input either doesn't matter at all, or needs to be at least as low as 8-bit.

like image 110
Nyerguds Avatar answered Aug 29 '26 18:08

Nyerguds



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!