Your company's ad could live here and reach over 50,000 people a month!

Share This Post

PowerShell / Scripting / Uncategorized

Bringing Back the DIP Switch: Saving Those Like-Settings as a Single (Bitwise) Value – Made Easy

Remember the DIP switch? One usually has to be "of a certain age" to vividly remember using those physically manipulated computer components that were there solely to configure Boolean settings for different hardware components. They usually came with 4 or 8 individual switches, but anywhere from 2 to 12 was not unheard of. A quick flick of one or more switch saved settings that configured how your hardware responded. I personally had one board once that the configuration of three dip switches changed the Baud rate that the board would use. The days of DIP switches are gone, but retaining settings certainly are not. Today’s settings are stored in configuration files or the registry, and although many script and program developers do write code that has configurable components, it can be surprising to see how often those settings are inefficiently stored, and could benefit from the lessons of the DIP switch.

Say you’re writing a code block that produces a 2D square, you want to allow the user to choose if the line is rendered for each side of the square, and you want those decisions retained as the default for subsequent renderings. A 2D square has 4 sides, and each side’s "draw a line" setting is either True (1) or False (0). This could be thought of, and stored as the following:

Top=1
Bottom=1
Left=0
Right=0

Quite often when I peer into settings storage, this is what I see: 4 related settings stored individually. When the code runs, it has to make 4 read calls to the settings storage just to complete one configuration. Open your favorite document editor or browser and access the options dialog. For my browser, I have 8 On/Off settings under Privacy alone. For my document editor, there are 5 On/Off settings just for printing. But when I access the Registry, I notice that there is but one setting for Printing Options, and it is a Binary value. Welcome back DIP switch.

A quick, simplified refresher. A Computer Bit is a single Boolean value: On/Off. A Computer Byte is 8 Bits, represented as a combination of zeros or ones: 00110101. Bytes are read from right to left (RtL), and each position doesn’t reflect the actual number value (zero or one), but is representative of a greater value: 256:64:32:16:8:4:2:1. To that, binary 00110101 has a decimal value of 53 (1+4+16+32). ). I also want to point out that I use the phrase "binary value" liberally in this article, despite often actually referencing a 32 or 64 bit integer value. I do this to keep focus on the fact that we are in fact manipulating these numbers on a binary level despite storing and viewing them as decimals.

Back to our square rendering example. Our settings for each side is binary, 0 or 1. Instead of looking at them as individual Bits, let’s look at them as a Byte (in the same order as listed above, using the RtL so "TBLR"): 1100. (OK, technically as a Byte, it would be 00001100 – but, we’ve only got 4 settings, so anything after the 4th position wouldn’t mean anything to us, and therefore we leave it out). This gives us a decimal value of 12. Now, because I’ve assigned each setting a Bit position, I only need to create one storage entry, and one read call to get all four settings. If you’re bored, you can jot down the 16 possible binary combinations (4 positions, 2 options: 2^4=16), and convert each binary number to it’s decimal value. What you will find is that the decimal number choices are "unique" within the whole set. Because you’re dealing with binary values, each resultant decimal number is significant and no possible combination of the decimal values will equal any other value within the whole set. Keep that in mind.

Let’s expand our current example. Our new theoretical code block does something (which we’re not concerned with for this demonstration), but we want to give the user an option of which days of the code block runs on. They can choose any combinations of the 7 weekdays. Learning from above, we don’t want to store that as 7 different settings. Instead, we will want to store it as a single setting. So as above with our four sides being assigned a bit position, let’s assign each day of the week a bit position. I happen to be in the US, and I culturally use Sunday as the first day of the week. So, my natural inclination is to view the week as "Su,M,T,W,Th,F,Sa". As such, I would then use that line up as my Bit position, and as above, I’ve only 7 options, so the 8th Byte position has no meaning:

256 64 32 16 8 4 2 1
Su M T W Th F Sa
0 0 0 0 0 0 0 0

Using the above reference then, Thursday is in bit position 4 (00000100) and has a decimal value of 4, and Sunday is in bit position 64 (01000000) and thus a decimal value of 64. If a user opted to only have the code block run on Thursday and Sunday, the resultant binary would be 01000100, with a decimal value of 68 (64 + 4).

And here is where (and why) my VBScript friends often go off the beaten track: Microsoft has an entire lineup of WeekDay constants, already assigned values. In that world, vbThursday has a decimal value of 5, and vbSunday a decimal value of 1. So the question I hear a lot why can’t we simply store a combination of those values. And the answer is because Microsoft has assigned actual decimal values to each constant, and the intent was to use those as a single instance comparison values. If you were to store actual decimal values in the same manner you store binary values, a combination of Thursday and Sunday would be 6. But in the MS constant world, 6 is the value for vbFriday. As actual decimal values when added are not unique, you would never know if 6 represented vbFriday, a combination of vbThursday +vbSunday, or even a combination of vbSunday + vbMonday + vbTuesday. True decimal values do not represent Bit positions within a Byte. When we store information as binary data, no combination of selected bit positions and the subsequent decimal value can ever total any other combination of selected bit positions.

Putting This to Use in Our Code

(This download contains working examples of all code in each language mentioned below.)

Enough talk, time for action. Just like in a human to human conversation, the people speaking often need to define common terms, so that everyone knows that when you say the word "grok", you are using a verb that means "understand (something) intuitively or by empathy". Because everyone in your conversation may not have read Robert A. Heinlein’s 1961 book "Stranger in a Strange Land", you need to ensure you don’t confuse them when you use that word in a sentence. The same goes for our weekday settings. We’re not using Microsoft’s decimal representation, so we need to tell everyone what our representations are, keeping in mind our bit positions as described above.

(I’ll provide examples in VB.NET, VBScript, PowerShell, and Jscript, but will truncate redundant information when that information would be easily understood. Reference the download for the full code.)

VB.NET
Const wdSATURDAY as Integer = 1
Const wdFRIDAY as Integer = 2
Const wdTHURSDAY as Integer = 4
Const wdWEDNESDAY as Integer = 8
Const wdTUEDSAY as Integer = 16
Const wdMONDAY as Integer = 32
Const wdSUNDAY as Integer = 64
            
VBScript
Const wdSATURDAY = 1
[…]
Const wdSUNDAY = 64
            
PowerShell
[int]$wdSATURDAY = 1
[…]
[int]$wdSUNDAY=64
            
JScript / JavaScript (Microsoft)
var wdSATURDAY=1;
[…]
var wdSUNDAY=64;
            

Our examples here are only to show how to "get" and "set" the resulting value, not how to store it in the registry or a configuration file. Although for the sake of simplicity I’ll mention that if you’re storing it in the registry, store it as a REG_DWORD value. Although some languages make reading/writing true REG_BINARY values easy, most don’t, and you don’t really need to most of the time. Plus, reading and writing a decimal value (you can do HEX if you want to get fancy) is easy to comprehend and otherwise work with.

As we’re only going to store our result in memory, we’ll need to create a storage variable as well:

VB.NET
Private wdSELECTION as integer = 0
            

(Follow suit for the other languages)

Now for the fun.

  • The user has selected Thursday and Sunday.
  • In our Byte representation the, we know that based on their choices, our weekday selection should be represented as: 1000100.
  • We know that we have to store a value which represents all 7 options.
  • We know that we have a decimal value of 4 for Thursday and 64 for Sunday.

Would simply adding the selected values be sufficient? You don’t always know what the user selected, which means you have to go through each option and determine if it was selected, and if it was, what the value of that selection is. In our VB.NET example, our form has a CheckBoxList that has each weekday as a selectable option. We then discover what the user selected using a code block similar to this:

VB.NET
For Each Item in CheckBoxList1.CheckedItems
    Select Case Item
        Case "Sunday": wdSELECTION = wdSELECTION + wdSUNDAY
        […]
        Case "Saturday": wdSELECTION = wdSELECTION + wdSATURDAY
    End Select
Next
            

This would technically leave wdSELECTION with the correct decimal value of 68. But we’re not really dealing with decimal values. We’re dealing with binary values. This simple decimal addition may actually leave us with the correct decimal value, but you couldn’t easily reverse the process via decimal subtraction to find what the user later deselected or amended.

Instead, we need to use math appropriate for adding binary values, which in this case is a bitwise operator, specifically XOR for VB.NET (XOR in VB.NET handles both logical and bitwise operations; this is not the case for all languages). I’ll spare you the entire conversation on bitwise operators and binary math. If you want the long run-down, start by reviewing Microsoft’s page on VB.NET’s XOR operator here which will lead you down a long and dark rabbit hole.

Suffice to say that instead of a decimal equation, we want a binary equation:

(Wrong:) (Correct:)
  04
+ 64
  ——
  68
  01000000
+ 00000100
  ————————–
  01000100

So our code block instead would look like this:

VB.NET
For Each Item in CheckBoxList1.CheckedItems
    Select Case Item
        Case "Sunday": wdSELECTION = wdSELECTION XOr wdSUNDAY
        […]
        Case "Saturday": wdSELECTION = wdSELECTION XOr wdSATURDAY
    End Select
Next
            
VBScript
Dim UserSelection: UserSelection = Array("Thursday","Sunday")

Dim strWeekday
For Each strWeekday In UserSelection
    Select Case strWeekday
        Case "Sunday"   : wdSELECTION = wdSELECTION Xor wdSUNDAY
        […]
        Case "Saturday" : wdSELECTION = wdSELECTION Xor wdSATURDAY
    End Select
Next
            
PowerShell
#(Note that PowerShell uses –xor for logical operations, 
#and –bxor for bitwise operations)

[string[]]$UserSelection = @("Thursday","Sunday")

ForEach ($WeekDay IN $UserSelection) {
    Switch ($WeekDay) {
        "Sunday"    { $wdSELECTION = $wdSELECTION -bxor $wdSUNDAY }
        [..]
        "Saturday"  { $wdSELECTION = $wdSELECTION -bxor $wdSATURDAY }
        }
    }
            
JScript / JavaScript (Microsoft)
//(Note that JavaScript uses a carat (^) for a bitwise XOR operation, 
//and a dual pipe (||) for a logical XOR operation. )
var UserSelection = ["Thursday","Sunday"];

for (i = 0; i < UserSelection.length; i++) {
    switch (UserSelection[i]) {
        case "Sunday"   : wdSELECTION = wdSELECTION ^ wdSUNDAY; break;
        […]
        case "Saturday" : wdSELECTION = wdSELECTION ^ wdSATURDAY; break;
        }
    }
            

Discovering the Settings

At this point, your code would store the value. Now let’s pretend that we are in the next user launch of our code, and we’ve retrieved it and now need to convert it back. Whereas the bitwise XOR operator was used to "add" the binary values, this operation is only to determine if a specific value is included within the binary value. What we are going to be asking is along the lines of: "In the Byte value 00100100, is the third bit activated (third bit is 00000100)?" To do this, we use the bitwise AND operator.

VB.NET
Dim DiscoveredSelections as String = ""

If wdSELECTION And wdSUNDAY Then DiscoveredSelections += "Sunday "
[…]
If wdSELECTION And wdSATURDAY Then DiscoveredSelections += "Saturday "
            
VBScript
If wdSELECTION And wdSUNDAY    Then WScript.Echo "Selection: Sunday"
[…]
If wdSELECTION And wdSATURDAY Then WScript.Echo "Selection: Saturday"
            
PowerShell
#(Note that PowerShell uses –and for logical operations, 
#and –band for bitwise.)
If ($wdSELECTION -band $wdSUNDAY)    { Write-Host "Selection: Sunday" }
[…]
If ($wdSELECTION -band $wdSATURDAY)    { Write-Host "Selection: Saturday" }
            
JScript / JavaScript (Microsoft)
//(Note that JavaScript uses the ampersand (&) for a bitwise AND.)
if (wdSELECTION & wdSUNDAY)    { WScript.Echo("Selection: Sunday"); }
[…]
if (wdSELECTION & wdSATURDAY)    { WScript.Echo("Selection: Saturday"); }
            

Changing a Selection

If you walk away from this article right now, you’ve at least got the basics of Getting and Setting a binary value. And that can really be all you need. For instance, I’ve a database that stores a decimal representation of a binary settings value; I only need to have it initially set, then display back the correct settings. That database value doesn’t change. But what if you need to change the user selected settings? For instance, you user initially selected Thursday and Sunday, but on the second run they want to add Wednesday, and on the third run they want to remove Sunday?

You always have the option of recreating the finished value each time, which might be the best choice if your selection options are minimal. But you also have the option of "editing" the value to include or remove new selections.

Adding new is simple. We did it above. You’ve your value that you recovered from storage and used to make the initial settings configuration (Thursday and Sunday). Then your user adds Wednesday. All you need to do then is take the existing value, and use the XOR operation to include the new value.

Then your user wants to remove Sunday. Turns out, you have two options.

Option One is that you do the exact same command as adding using XOR. This will act as a toggle, turning the bit ON if it find it OFF, and vice versa.

Option Two is more "Off Only". With this option, you use a combination of the bitwise AND and bitwise NOT. Bitwise NOT will reverse the binary value (e.g., changes 0101 to 1010) and then when used in conjunction with AND, you wind up with only the desired bit removed.

VB.NET
wdSELECTION= wdSELECTIONAnd (Not wdSUNDAY)
            
VBScript
wdSELECTION = (wdSELECTION And (Not wdSUNDAY))
            
PowerShell
$wdSELECTION = ($wdSELECTION -band (-bnot $wdSUNDAY))
            
JScript / JavaScript (Microsoft)
//(Note that JavaScript uses the tilda (~) for bitwise NOT operations)

wdSELECTION = (wdSELECTION & (~ wdSUNDAY));
            

A Real World Example

A great real world example that makes heavy use of this method of bitwise settings add/remove is actually the Windows NTFS File System. Files come with attributes, such as ReadOnly, Archive, System, etc., that are stored with the file system object as a binary setting.

When you programmatically access a file system object, you use the bitwise AND to identify which of those attributes are currently set. If you wish to add a new attribute, you use the bitwise XOR to add/toggle the attribute. To remove, the bitwise combination of AND and NOT.

I’m not including any code examples here, but the download contains a PowerShell script which shows not only the above points, but includes a few quick snippets that show you all the actual attributes that a file system object can support, using just a few lines of PowerShell.

A Quick Reminder on Size

Our base examples in this article focus on values that are relatively small. Our 2D square used only 4 bits, and our weekday example only 7 bits. From a decimal standpoint, these are 32-bit integers, which means you can toggle up to 32 Bits or 4 Bytes. After that, you move into 64-bit integers, which require a little more planning to deal with. These numbers allow you to work with 64 Bits or 8 Bytes. 64-bit integers also require addition consideration when you’re attempting to use them on 32-bit operating systems, as the OS has to jump through a few hoops to process those numbers, and not all operations have the expected outcome. In short, try to keep it to 32-bit integers for this style of settings retention whenever possible. The below PowerShell script gives a quick demo of sizing (and is included in the download).

PowerShell
#Int32

"$([int32]::MinValue) to $([int32]::MaxValue)"

$Integer32 = 0xFF
$Integer32.GetType()

$Integer32 = 0xFFFF
$Integer32.GetType()

$Integer32 = 0xFFFFFF
$Integer32.GetType()

$Integer32 = 0xFFFFFFFF
$Integer32.GetType()

[System.Convert]::ToString($Integer32,2)

#Int64

"$([int64]::MinValue) to $([int64]::MaxValue)"

$Integer64 = 0xFFFFFFFFFF
$Integer64.GetType()

$Integer64 = 0xFFFFFFFFFFFFFFFF
$Integer64.GetType()

[System.Convert]::ToString($Integer64,2)
            

Wrapping Up

I’ve written many scripts and programs that need to know when it appropriate to run, be that weekdays, months, hours, etc. The thought of having to code any process that would manage 7, 12, or 24 individual settings storage objects would be a nightmare, when you could store all three of those examples, in any configuration, in as little as one settings entry.

You already know two processes that store or report settings in this binary fashion, Windows NTFS file system attributes for objects, and RoboCopy exit codes. In fact, whenever you see a setting or return code that matches bit positions, you know what you’re dealing with.

Why not harness the same simplicity for yourself!

Share This Post

A senior architect with over 16 years of experience in desktop design, delivery and production management. 14 years of law firm-centric experience in developing, integrating, and implementing robust and full-featured desktop solutions, focused on solid Microsoft and Microsoft partner platforms crafted to deliver an optimal fit for the environment.

Leave a Reply