Tuesday, October 20, 2015

LotusScript: Bitmasks

Originally published 

Sat 18 Nov 2006

Editors Notes
I decided to pull this one out of the archive because it presents a clear lesson on bitmasks, how to make use of them and hopefully how to understand them.  The function presented is really only for those purposes, being difficult to use in practice due to the time consumed figuring out which things you want to add together to get the desired result.

Original Post

Bitmasks are useful little items to have in your coders toolbox. A bitmask allows you to extract multiple on / off flags from one integer. You may have seen this when working with some Lotus Script built-in functions, like MessageBox, where you can pass an integer or addition of integers in to specify icon and button options. Here I aim to show you how it works and explain it a bit (no pun intended.)

Let's look quickly at what bits are. Bits are the 1's and 0's of binary language. They are at the lowest level of all programming languages, representing high and low voltages, in many cases, within your computer's memory. Back when the Commodore 64 came out, it used an 8-bit operating system. In that environment, the largest integer you can represent with a single byte (made of 8 bits) would be 2^0 - 2^7 (0 to 255). The important thing to understand is that the bits represent powers of 2, usually starting from right to left with 2^0.

 
Bit value   = 128    64    32    16    8    4   2   1
Bit setting = 0      0     1     0     0    1   0   0
Total Value =              32       +       4     =    36
This is an easy example, and one you have probably used with the MessageBox function (MB_ICONQUESTION + MB_YESNO). Basically the bits tell us that 32 and 4 are on or high, for a total of 36. If we are passing this value to a function, we can just use 36 and it will know via logical comparison that we meant 32 + 4 or 00100100.

Now we hopefully understand how LS uses this internally, to a degree. How do we make use of this same power? Wouldn't it be nice to create a function that takes one value as opposed to a myriad of flags? Just think how much more compressed a function like DialogBox could be without all those true and false flags.

So, that sounds like a nice demonstration - let's write a more condensed wrapper for Workspace.DialogBox. It's not terribly useful or efficient, but it's a quick way to show you how to use a bitmask and provide a template so you can employ it with your own functions. The current function definition appears below.

flag = notesUIWorkspace.DialogBox( form$ , autoHorzFit , autoVertFit , noCancel , noNewFields , noFieldUpdate , readOnly , title$ , notesDocument , sizeToTable , noOkCancel , okCancelAtBottom )
What a mess! Many of those optional parameters are accepted as boolean values, true or false, which maps logically to binary, on or off. In our example below, we are arbitrarily assigning a bit position in a byte of suitable length to each flag. We'll use the And operator of LotusScript to determine which flags are on or off based on the receipt of a single value (a long in this case).

Function UIDialog(sForm as string, sTitle as String, nDoc as NotesDocument, hFlags as Long) as integer
Dim wk as new NotesUIWorkspace

' Set up our bit registers and note their arbitrary values
Dim autoHorz as boolean ' 1
Dim autoVert as boolean ' 2
Dim noCancel as boolean ' 4
Dim noNewFields as boolean ' 8
Dim noFieldUpdate as boolean ' 16
Dim readOnly as boolean ' 32
Dim sizeToTable as boolean ' 64
Dim noOkCancel as boolean ' 128
Dim okCancelAtBottom as boolean ' 256

' set our registers based on the passed in flags value. the AND comparison will only evaluate TRUE if the bit compared to the flag can be factored out of it.
autoHorz = 1 And hFlags
autoVert = 2 And hFlags
noCancel = 4 And hFlags
noNewFields = 8 And hFlags
noFieldUpdate = 16 And hFlags
readOnly = 32 And hFlags
sizeToTable = 64 And hFlags
noOkCancel = 128 And hFlags
okCancelAtBottom = 256 And hFlags

' now we call our wrapped target with all the gory flags
UIDialog = wk.DialogBox(sForm, autoHorz, autoVert, noCancel, noNewFields, noFieldUpdate, readOnly, sTitle, nDoc, sizeToTable, noOkCancel, okCancelAtBottom)
End Function


Now, for the payoff for our extra effort. Lets say we want to call a dialog box that fits to a table in a form called "MyTableDialog" which has it's own OK and Cancel button built in. For size to table to work right, I also need to specify autoHorizFit and autoVertFit. NoOKCancel by itself produces just a Cancel button, oddly, so I'll add the flag for NoCancel as well.

call UIDialog("MyTableDialog", "Some nice Title", uidoc.document,1+ 2 + 4 + 64 + 128)
I showed the component flags expressed as addition, but we could have written it as 199 for even more brevity. When the function executes, each And operation will evaluate to False except where our mask will allow the flags arbitrary value to pass. The values we masked against the hFlag property for each of these can be factored out as powers of 2. What this means in that any random value between 0 and 511 will be acceptable and will turn on or off an appropriate number of flags. The only draw back is we have to consult the code or documentation to use this kind of flag compression. Otherwise, it's easy to implement and provides quite a bit of power.

No comments:

Post a Comment