Flags

 

User’s Guide

 

Version 2.0

 

 

Flags provide a mechanism for supporting bit flags and bitwise operations beyond the limitations of the normal TADS 3 32-bit infrastructure.

 

Copyright © 2005 by Kevin Forchione. All rights reserved.


Contents

 

Working with Flags and Bit Flags.. 3

BitFlag Class.. 5

Creating Bit Flags. 5

DefBitFlag Macro.. 5

Flag Class.. 6

Flag Validation.. 7

Creating Flags. 8

Converting Between Bit Strings and Flags. 9

The cvtBitString() Method. 9

The toBitString() Method. 9

The disp() Method. 10

Setting and Clearing Flag Bits. 11

The setBits() Method. 11

The clearBits() Method. 11

Operating on Bits. 12

Using Bitwise Methods. 13

Using Shift Methods. 13

Evaluating Flags. 14

The ival() Method. 14

The lval() Method. 14

Comparing Flags. 16

The equals() Method. 16

The comp() Method. 16

Counting Bits. 17

 

 


Working with Flags and Bit Flags

Flags provide a compact way of storing, evaluating, and manipulating object characteristics.

 

The relationship between bit number and bit value is as follows:

 

bit0 => 20 = 1

bit1 => 21 = 2

bit2 => 22 = 4

bit3 => 23 = 8

. . .

 

 

    For n bits:

The low order bit is indexed at 0

The high order bit is indexed at n - 1.

 

 

The usual approach to defining bit flags is to use #defines and assign integer value powers of 2:

 

#define     TAKEBIT           1     // 20

#define     TRYTAKEBIT        2     // 21

#define     CONTBIT           4     // 22

#define     DOORBIT           8     // 23

#define     OPENBIT           16    // 24

. . .

 

And so on until we reach the limitations of an integer value.

 

We could then model the state of an open door like this:

 

door: object

{

      state = (DOORBIT|OPENBIT)

}

 

We’ve assigned the state by combining two bit flags using the OR bitwise operator. We could then interrogate the door’s state to determine if it were open by using the AND bitwise operator, as follows:

 

If ((door.state & OPENBIT) != 0)

      do-something;

 

As we noted, up to 31 bit flags can be defined in this way before the maximum integer value is reached. It sounds like this ought to be more than enough for any definition. But when using flags to keep track of object characteristics we can quickly bump up against the 32-bit limit. For instance, ZIL employed at least 34 flags as documented in the ZIL.pdf.

 

The TADS 3 Flags library extension provides the means for an author to work with bit flags beyond the 32-bit limitation.


BitFlag Class

The BitFlag class derives from the Flag class, specialized to allow ease of definition. In this implementation a bit flag is an instance of Flag that has 1 and only 1 bit turned “on”.

 

Creating Bit Flags

There are two ways to create instances of BitFlag. You can either statically define the flag, assigning it a bitNum value, like this:

 

TakeBit: BitFlag

      bitNum = 0

;

 

or you can create one dynamically by using the new operator and passing the class a bitNum value, like this:

 

new BitNum(3)

 

DefBitFlag Macro

Because static definitions of bit flags are so common and useful, the library extension provides a macro for compactly defining static bit flags. Using the macro, the equivalent of the bit flag #defines above would be:

 

TakeBit:    DefBitFlag(0);    // 20

TryTakeBit: DefBitFlag(1);    // 21

ContBit:    DefBitFlag(2);    // 22

DoorBit:    DefBitFlag(3);    // 23

OpenBit:    DefBitFlag(4);    // 24

. . .

 

The DefBitFlag() macro defines the superclass of each bit flag, and indicates which bit is to be turned “on” in the bit flag. In the above examples the #define for TAKEBIT has a value of 1, while the DefBitFlag() macro for TakeBit passes ‘0’ as its argument.

 

In addition, the macro will throw a compile-time exception if the bitNum is greater than that allowed by the particular implementation of Flags (the default is 63).

 


Flag Class

The Flag class encapsulates a collection used to store bit values. This allows the Flag class to overcome the 32-bit limitation. Although the default implementation allows for only 64 flags, this limit is modifiable, and since the collection has an upper limit in the millions of bytes, for practical purposes there is no limitation to the number of bit flags an author can define.

 


Flag Validation

For the sake of efficiency, Flags doesn’t validate the ranges for bit numbers, bit strings, or element numbers in its computations unless you tell it to do so. The DefBitFlag macro will automatically throw a compile-time error if the bit number is greater than the maximum allowed by the implementation. But this is the only range-checking done by the implementation unless it is specifically told to do so.

 

If you want this further safeguard, you must specifically tell Flags to validate ranges for bit numbers and elements by defining  __FLAG_VALIDATE__ in your compile.

 

In the majority of cases this won’t be necessary, since most of the referencing by bit number and element number are internal to the extension’s computation methods. In most of the cases where you could reference an invalid bit number or element number value, the system will throw an exception error at the lower level of its implementation.

 


Creating Flags

To create an instance of Flag class you use the new operator, and pass its constructor either a single-quote string of the binary representation of its value or a series of flags. For instance, suppose we have the following bit flags defined:

 

TakeBit:    DefBitFlag(0);    // 20

TryTakeBit: DefBitFlag(1);    // 21

ContBit:    DefBitFlag(2);    // 22

DoorBit:    DefBitFlag(3);    // 23

OpenBit:    DefBitFlag(4);    // 24

 

We could represent the state of an open door with the following flag:

 

state = new Flag(DoorBit, OpenBit);

 

Or we could create this flag using a “bit string” representation. If we use the “bit string” approach we don’t need to supply high-order zeros.

 

state = new Flag(‘11000’);

 

We can create a “bit string” representation for any existing flag simply by passing it the toBitString() message, indicating whether or not we want a space separating each byte:

 

str = new Flag(‘11000’).toBitString(true);

 

The length of the string will be determined by the Flag class’ collSize attribute, which is set to a default of 2. This will allow Flag to accommodate up to 64 bits of information (The maximum number of bits in an implementation can be determined by adding 1 to the Flag class’ maxBitNum attribute).

 


Converting Between Bit Strings and Flags

Creating flags from “bit strings” and converting flags to “bit strings” is a handy convenience, both in making a flag humanly readable, and in providing a useful shortcut in designating the bits of a flag.

 

There is no native representation of the series of bits that represent a flag. Instead the Flags library extension makes use of single-quoted strings consisting of binary values, separating bytes, if desired, by spaces.

 

So, for instance, this is a “bit string”:

 

‘111’

 

and so is this:

 

‘00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000111’

The cvtBitString() Method

A flag can be created from a “bit string” representation, such as our new Flag(‘111’) example. The method that actually performs this conversion is the cvtBitString() method.

 

This method returns a new flag.

 

It ignores whitespace within the bit string, and counts only non-whitespace characters to determine the bit number to set. The method only sets those bits whose corresponding “bit string” values are ‘1’.

 

An example of the syntax is:

 

flag = Flag.cvtBitString(‘1110 0111’)

The toBitString() Method

This method converts the bit values of a flag into a “bit string” representation. The “bit string” consists of all the bits of the flag (represented by maxBitNum +1). The method takes one optional argument: byteSep, which if true will separate each byte of bits by a single whitespace character in the string.

 

For example, for the default (64-bit) implementation:

 

new flag(‘111’).toBitString(true)

 

will return a “bit string” that looks like this:

 

‘00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000111’

The disp() Method

When this method is called it will convert the flag to a “bit string” value and display it. This method is intended for debugging purposes.

 


Setting and Clearing Flag Bits

Both setBits() and clearBits() apply their results to the flag receiving those messages and return self. Both methods can take multiple flag arguments, applying the results recursively to the flag receiving the message.

 

Because these methods return self, they can be used like the bitwise methods and we can chain messages, as in the following example:

 

new Flag('111').clearBits(new Flag('10')).disp();

 

The setBits() Method

This method sets the corresponding bits of the receiving flag “on” if they are “on” for the flag argument. No action is taken for those bits of the argument flag that are not turned “on”.

 

flag1.setBits(flag2)

 

is equivalent to

 

flag1 = flag1.or(flag2)

 

The clearBits() Method

This method sets the corresponding bits of the receiving flag “off” if they are “on” for the flag argument. No action is taken for those bits of the argument flag that are not turned “on”. This allows us to specify with bit flags, those bits of the receiving flag that we want to clear.

 

flag1.clearBits(flag2)

 

is equivalent to

 

flag1 = flag1.and(flag2.not())

 

One caveat: suppose we wanted to clear the DoorBit and OpenBit values for flag. Using flag.clearBits(DoorBit, OpenBit) wouldn’t work properly because it would clear all the bits, regardless of the flag’s bit values. To clear only the DoorBit and OpenBit we need to do the following: flag.clearBits(new Flag(DoorBit, OpenBit)).

 

 


 

Operating on Bits

Because flags and bit flags are objects, we can’t use bitwise operators on instances of the Flag class. We have to use the equivalent Flag methods.

 

Flag class provides methods that allow an author to perform bitwise and shift operations. Like BigNumber objects, the Flag object has specialized bitwise methods that replace the native bitwise operators.

 

These bitwise methods of Flag class don’t return integer values like bitwise operators do, they return Flag instances.

 

Bitwise Operator

Flag Method

Meaning

>>

rsh()

Shifts RIGHT by the number of bits specified; zero padded left.

<<

lsh()

Shifts LEFT by the number of bits specified; zero padded right.

!

not()

Bitwise NEGATION

&

and()

Bitwise AND

|

or()

Bitwise OR

^

xor()

Bitwise XOR

 

 

 

The bitwise methods of Flag class don’t return integer values like bitwise operators do, they return Flag instances.

 

Messages to the Flag class can be chained. Chained messages are executed from left to right.

 

 

For example:

 

new Flag(‘111’).and(bit1).or(bit3).disp()

 

is equivalent to the following:

 

flag = new Flag(‘111’);

flag = flag.and(bit1);

flag = flag.or(bit3);

flag.disp();

 

Using Bitwise Methods

The Flag bitwise methods not(), and(), or(), and xor() all take flag arguments. The and(), or(), and xor() methods can take multiple flag arguments, which are recursively applied to the results of the previous argument. For instance:

 

flag.and(DoorBit, OpenBit).lval() would return nil in every case because it is equivalent to ((flag & DoorBit) & OpenBit). If you want to check if the flag has both DoorBit and OpenBit set you would code it as flag.and(new Flag(DoorBit, OpenBit)).lval().

 

The not() bitwise method takes no argument. It returns the complement of the flag. For instance, new Flag(‘1010’).toBitString(true) would return:

 

‘11111111 11111111 11111111 11111111 11111111 11111111 11111111 11110101’.

 

Using Shift Methods

The shift methods return a flag that has its bits shifted either left or right the indicated distance. For instance new Flag(‘1111’).lsh(4).toBitString(true) will return a string value ‘00000000 00000000 00000000 00000000 00000000 00000000 00000000 11110000’. Both left and right shifts pad with zeros.

 


Evaluating Flags

Much of the time we want to evaluate the results of a bitwise operation to an integer or logical value. A common example is to interrogate a flag for a certain bit value:

 

if ((flag & TakeBit) != 0)

      do-something;

 

We can’t use bitwise operators on the Flag class, we have to use the equivalent method. But the bitwise methods in Flag class don’t return integer values, they return Flag instances.

 

Method

Purpose

ival([bitNum])

Returns 1 to indicate that the flag has at least one bit turned “on” otherwise returns 0. If a bitNum is passed then it evaluates the specified bit in the flag.

lval([bitNum])

Returns true to indicate that the flag has at least one bit turned “on” otherwise returns nil. If a bitNum is passed then it evaluates the specified bit in the flag.

 

The ival() Method

The ival() method examines the bits of an instance of Flag and returns an integer value (1/0) indicating whether or not at least one of the bits of the flag is “on”.

 

So, one way to code the equivalent interrogation of flag for TakeBit using the Flag ival method would be:

 

if (flag.and(TakeBit).ival() != 0)

      do-something;

 

 

The lval() Method

The lval() method examines the bits of an instance of Flag and returns a logical value (true/nil) indicating whether or not at least one of the bits of the flag is “on”.

 

So, one way to code the equivalent interrogation of flag for TakeBit using the Flag lval method would be:

 

If (flag.and(TakeBit).lval())

      do-something;

 

 


Comparing Flags

Sometimes we want to compare a flag, or the results of a bitwise method to another flag. A typical example is as follows:

 

if ((flag & TakeBit) == TakeBit)

      do-something;

 

As we can’t use bitwise operators on the Flag class, we have to use the equivalent method. But the bitwise methods in Flag class don’t return integer values, they return Flag instances.

 

The equals() Method

If you wanted to compare the result of a bitwise method to a flag, you could simply use the equals() method, like this:

 

if (flag.and(TakeBit).equals(TakeBit))

      do-something;

 

The equals() method returns a logical value (true/nil) to indicate whether the two flags have equal bit values.

 

The comp() Method

This method returns the following integer values:

 

Return Value

Meaning

-1

The bit values of the flag receiving the &comp message are less than the bit values of the flag in the argument

0

The bit values of the flag receiving the &comp message are equal to the bit values of the flag in the argument

1

The bit values of the flag receiving the &comp message are greater than the bit values of the flag in the argument

 

You would code the comparison similarly to the following:

 

if (flag.and(TakeBit).comp(TakeBit) == 0)

      do-something;

 


Counting Bits

You can count the number of bits in a Flag, or in an element of a flag, by using the bitCount() method. This method will return an integer value of the number of “on” bits for the Flag or the element in question.

 

For instance:

 

      new Flag(‘101’).countBits()

 

would return 2; while

 

      new Flag(bit33, bit32, bit31).countBits(1)

 

would return 2.