2

I am using a Flags Enum to track the completion stages of a data migration process for each data record. I need a way to reset back to a specified stage where I can begin reprocessing the migration of a data record. How does one reset the higher bytes in a Flags Enum?

Example Enum:

[Flags]
public Enum MigrationStages {
  None = 0,
  Started = 1,
  MiddleStage = 2,
  WrappingUp = 4,
  Finished = 8
}

My current value:

var currentStage = 
    MigrationStages.None 
    | MigrationStages.Started 
    | MigrationStages.MiddleStage
    | MigrationStages.WrappingUp
    | MigrationStages.Finished;

I want to reset back to MigrationStages.MiddleStage to cause reprocessing to occur starting there.

Jack Pines
  • 473
  • 4
  • 13

2 Answers2

3

Bitwise math is not something we use much anymore. As such, when I went searching for an answer to this I found nothing that helped so I worked it out. Sharing my math with the world in case others find it useful.

I created a simple helper method to do this, as follows:

public static MigrationStage ClearHigherFlags(MigrationStage orig, MigrationStage highBit)
{
    var lowerBits = (int)orig % (int)highBit;
    return highBit + lowerBits;
}

Usage example:

currentStage = ClearHigherFlags(currentStage, MigrationStages.MiddleStage);

Obviously, if you want to clear higher flags including the highBit, just don't add it back. To clear lower flags, return orig - lowerBits.

In bitwise math, modulus (%) is often your friend.

Addendum

There are those who will find this answer and think that it's not really bit math. I hope this assuages those folks.

First, recall that this is flags we're talking about so a very specific subset of bit manipulation where modulus makes the math easier to read and is very appropriate. The actual math performed by the compiler replacement will be something like what follows, which I find much less intuitive to read.

public static MigrationStage ClearHigherFlags(MigrationStage orig, MigrationStage highBit)
{
    var bitMask = highBit - 1;
    var lowerBits = orig & bitMask;
    return highBit + lowerBits;
}

It's really not too hard to read but the conversion to a bit mask is done implicitly in my original solution.

Jack Pines
  • 473
  • 4
  • 13
  • That would really confuse me. Why not a good old `&`? I'd say `%` is the opposite of a friend in bit math, it only just happens to be equivalent to bit math when the divisor is a power of two, so it looks out of place until one notices that the divisor is a power of two and even then it can be most easily understood by mentally interpreting it as a bitwise AND anyway – harold Apr 22 '17 at 12:51
1

If you want to use bitwise manipulation you can do it this way:

var lowbits = MigrationStages.MiddleStage | MigrationStages.Started;

Then to clear the high bits in your example:

currentStage = currentStage & lowbits;

Maybe this will make more sense:

               8  4  2  1
               ==========
lowbits        0  0  1  1
currentvalue   1  1  1  1
               ==========
AND (&)        0  0  1  1

which clears the two high bits

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
Deolus
  • 317
  • 5
  • 13
  • I don't follow you. You haven't shown where `highBit` comes into play. – Jack Pines Apr 22 '17 at 01:56
  • The problem is that I only know the high bit to stop at at run time. I don't know the low bits to keep. I mean, the computer doesn't. You see, I simplified the problem to the point where it is not obvious that I may skip steps in the migration or that they may be run asynchronously so not all the lower ones may have run. So, I can't & all the bits lower than highBit. The answer must include highBit. – Jack Pines Apr 24 '17 at 13:04
  • I hope my update makes it clearer and incorporates what you were trying to suggest, @Deolus. – Jack Pines Apr 24 '17 at 18:14
  • @JackPines In your original question you only wanted to revert back to `MigrationStages.Middlestage` so what does it matter what the high bits are? In your addendum it looks like you are not changing `orig` at all. – Deolus Apr 25 '17 at 12:40
  • 1
    @JackPines Ok I did more experimenting with .NET Fiddle (https://dotnetfiddle.net/XuPDS6) and I can see what you are trying to do. The addendum is the way I would have done it. – Deolus Apr 25 '17 at 12:58