[Script] Bit Flag Helper

Discuss and share scripts and script files...
Post Reply
TheQwerty
Posts: 4373
Joined: 03 Aug 2007 22:30

[Script] Bit Flag Helper

Post by TheQwerty »

Another script from a couple months ago.

This is three main scripts that are useful for working with bit-flags until Don blesses use with some bitwise operators. ;)

Included Scripts:
Demo -- demo
Just a small demo of this library's capabilities.

Combine Flags Example -- combineExample
Extract Flags Example -- extractExample
Expand Flags Example -- expandExample
These just show some simple code examples of using the scripts.


Combine Flags -- _combine
Converts a list of global flag/boolean variable names into an OR-ed bitwise value.

Extract Flags -- _extract
Takes an OR-ed bitwise value and a list of global flag/boolean variable names and it will set them appropriately.

Expand Flags -- _expand
Takes an OR-ed bitwise value and sets some generic global flag/boolean variables appropriately.
$G_BIT_COUNT gets the count of flag variables.
$G_BIT_1 through $G_BIT_N are the flag variables.
BitFlag.xys
v1.0 - October 29, 2014.
(15.64 KiB) Downloaded 228 times

Code: Select all

/*#############################################################################\
BitFlag.xys

Library for working with bit flags.

[ABOUT]
Author    = TheQwerty
Version   = 1.0
Date      = 2014-10-29 19:30z
Requires  = XYplorer v14.50.0000

[]
\#############################################################################*/


/*******************************************************************************
** Demo
**   Walks through a small demo of this library's scripts.
*******************************************************************************/
"&Demo||1 : demo"
	// Prompt for flag value to expand/extract.
	Global $G_FLAGS = Input('OR-ed Bitwise Value', "Enter the value to examine<crlf>This can even be a simple mathematical equation. i.e. '1+2+4'", '1+2+16', 's');

	// Accept expressions by evaluating input.
	$flagValue = $G_FLAGS;
	$G_FLAGS = Eval($G_FLAGS);

	// Expand G_FLAGS to G_BIT_# vars.
	Sub '_expand';
	Global $G_BIT_COUNT;

	// Collect G_FLAGS information & expand header.
	$result = 'G_FLAGS = ' . $flagValue;
	if ($flagValue != $G_FLAGS) {
		$result = $result . " = $G_FLAGS";
	}
	$result = $result . "<crlf 2>Results of Expand Flag:<crlf>G_BIT_COUNT = $G_BIT_COUNT<crlf>";
	Unset $flagValue;

	// Collect expand results.
	$i = 1;
	while ($i <= $G_BIT_COUNT) {
		$var = '$G_BIT_' . $i;
		Global *$var;
		$result = $result . $var . ' = ' . (*$var) . "<crlf>";
		$i++;
	}
	Unset $i, $G_BIT_COUNT, $var;

	// Prompt for variables to extract to/combine.
	Global $G_VARS = Input('Variable Names', "Enter a |-separated list of variable names to store flags in.<crlf>The leading '$' is optional and unwanted flags can be left empty.", '$oneSet|$twoSet|$fourSet||$sweetSet', 's');

	// Extract G_FLAGS to G_VARS.
	Sub '_extract';

	// Collect extract header.
	$result = $result . "<crlf 3>Results of Extract Flags:<crlf>G_VARS = " . $G_VARS . "<crlf 2>";

	// Collect extract results from specified vars.
	foreach ($var, $G_VARS, '|') {
		if ($var == '') { continue; }
		if ($var UnLikeI '$*') { $var = '$' . $var; }
		Global *$var;
		$result = $result . $var . ' = ' . (*$var) . "<crlf>";
	}
	Unset $var;

	$G_FLAGS = 0;

	// Run combine on G_VARS.
	Sub '_combine';

	// Collect combine header & results.
	$result = $result . "<crlf 3>Results of Combine Flags:<crlf>G_FLAGS = " . $G_FLAGS . "<crlf>Note that if G_VARS does not include all set bits this value will not match the original.";

	// Share results.
	Text $result;
/******************************************************************* END demo */


"-" //--------------------------------------------------------------------------


/*******************************************************************************
** Combine Flags Example
**   Show usage example.
*******************************************************************************/
"&Combine Flags Example : combineExample"
	$myPath = Self('file');
	if ($myPath != '') {
		$myPath = ResolvePath($myPath, <xyscripts>, 1);
	} else {
		$myPath = 'BitFlag';
	}

	Text <<<EXAMPLE
The following example demonstrates how to use the Combine Flags script to
convert a list of global binary flag variables into a single OR-ed value.

_combine parameters:

Input: Global $G_VARS
    A |-separated list of variable names to combine.
      - Each variable must be global and should be set to true (1) or false (0).
      - The list starsts with bit-value 1, then 2, 4, 8, 16, etc.
      - Omitting variable names (not positions) treats those as false.
      - The leading '$' is optional.
      - Variable names are trimmed of spaces before use.
      - Note that if a variable shares a name with one used locally by _combine
        an error will occur.

Output: Global $G_FLAGS
    The result of OR-ing each flag variable within $G_VARS.
--------------------------------------------------------------------------------

"Combine Flags Example"
  // Use global flag variables.
  Global $hasXYplorer = true;  // Bit value: 1
  Global $hatesXY = false;     // Bit value: 2
  Global $isAwesome = true;    // Bit value: 4

  // CALL TO COMBINE:
  // Input: List of global variable names to combine.
  Global $G_VARS = 'hasXYplorer|hatesXY|isAwesome';
  // Make sure to adjust the path to BitFlag if needed.
  Load '$myPath', '_combine', 'f';
  // Return:
  Global $G_FLAGS;

  Echo "The OR-ed value is $G_FLAGS - it should be 5.";
EXAMPLE
	, /*width*/, /*height*/, 'Example usage of Combine Flags';
/********************************************************* END combineExample */


/*******************************************************************************
** Extract Flags Example
**   Show usage example.
*******************************************************************************/
"&Extract Flags Example : extractExample"
	$myPath = Self('file');
	if ($myPath != '') {
		$myPath = ResolvePath($myPath, <xyscripts>, 1);
	} else {
		$myPath = 'BitFlag';
	}

	Text <<<EXAMPLE
The following example demonstrates how to use the Extract Flags script to
set a list of global binary flag variables from a single OR-ed value.

_extract parameters:

Input: Global $G_FLAGS
    An OR-ed value to extract to the variables specified in G_VARS.

Input: Global $G_VARS
    A |-separated list of variable names to set.
      - Each variable will be made global and set to true (1) or false (0).
      - The list starts with bit-value 1, then 2, 4, 8, 16, etc.
      - Omitting variable names (not positions) will skip those bits.
      - The leading '$' is optional.
      - Variable names are trimmed of spaces before use.

Output:
    The variables specified in G_VARS will be made global and set accordingly.
--------------------------------------------------------------------------------

"Extract Flags Example"
  // Existing flag value.
  $flag = 5;

  // CALL TO EXTRACT:
  // Input: Flag value.
  Global $G_FLAGS = $flag;
  // Input: List of global variables to set.
  Global $G_VARS = 'hasXYplorer|hatesXY|isAwesome';
  // Make sure to adjust the path to BitFlag if needed.
  Load '$myPath', '_extract', 'f';
  // Returns:
  Global $hasXYplorer;
  Global $hatesXY;
  Global $isAwesome;
  
  Echo <<<MSG
Has XYplorer = $hasXYplorer
Hate It = $hatesXY
Is Awesome = $isAwesome
  MSG;
EXAMPLE
	, /*width*/, /*height*/, 'Example usage of Extract Flags';
/********************************************************* END extractExample */


/*******************************************************************************
** Expand Flags Example
**   Show usage example.
*******************************************************************************/
"E&xpand Flags Example : expandExample"
	$myPath = Self('file');
	if ($myPath != '') {
		$myPath = ResolvePath($myPath, <xyscripts>, 1);
	} else {
		$myPath = 'BitFlag';
	}
	$fakeCRLF = '<crlf>';

	Text <<<EXAMPLE
The following example demonstrates how to use the Expand Flags script to
expand a single OR-ed value into a set of global variables.

_expand parameters:

Input: Global $G_FLAGS
    An OR-ed value to extract into a set of flag variables.

Output: Global $G_BIT_COUNT
    The number of global G_BIT_# variables used.

Output: Global $G_BIT_#
    For each bit-value there will be a corresponding $G_BIT_# variable.

Example output for G_FLAGS = 1+4
    G_BIT_COUNT = 3
    G_BIT_1 = 1    // Flag 1
    G_BIT_2 = 0    // Flag 2
    G_BIT_3 = 1    // Flag 4
--------------------------------------------------------------------------------

"Expand Flags Example"
  // Existing flag value.
  $flag = 5;

  // CALL TO EXPAND:
  // Input: Flag value.
  Global $G_FLAGS = $flag;
  // Make sure to adjust the path to BitFlag if needed.
  Load '$myPath', '_expand', 'f';
  // Return: Count of global bit variables set.
  Global $G_BIT_COUNT;

  $i = 1;
  $msg = '';
  while ($i <= $G_BIT_COUNT) {
    // Return: For each bit there is a G_BIT_# global variable.
    $varName = '$G_BIT_' . $i;
    Global *$varName;
    $msg = $msg . 'G_BIT_' . $i . ' = ' . (*$varName) . "$fakeCRLF";
    $i++;
  }
  Echo $msg;
EXAMPLE
	, /*width*/, /*height*/, 'Example usage of Expand Flags';
/********************************************************** END expandExample */


"- : _-" //---------------------------------------------------------------------
"- : _-" //---------------------------------------------------------------------


/*******************************************************************************
** Combine Flags
**   Converts a set of global flag variables into an OR-ed bitwise value.
*******************************************************************************/
"Combine Flags||4 : _combine"
	// -------------------- PARAMETERS -------------------------------------------
	// IN: G_VARS
	//  A |-separated list of variable names to combine.
	//  - Each variable must be global and should be set to true (1) or false (0).
	//  - The list starts with bit-value 1, then 2, 4, 8, 16, etc.
	//  - Omitting variable names (not positions) treats those bits as false.
	//  - The leading '$' is optional.
	//  - Variable names are trimmed of spaces before use.
	//  - Note that if a variable share a name with one used locally by this
	//    script an error will occur.
	//  Examples:
	//      // Combines flags one, two, and four.
	//      $G_VARS = '$flagOne|$flagTwo|$flagFour';
	//      // Combines flags two (checkboxes) and eight (genericIcons) only,
	//      //   treats flags one and three as false.
	//      $G_VARS = '|checkboxes||genericIcons';
	Global $G_VARS;

	// OUT: G_FLAGS
	//  The OR-ed bitwise value of the global variables in G_VARS.
	Global $G_FLAGS = 0;
	// -----------------END PARAMETERS -------------------------------------------

	// Parameter validation.
	Assert $G_VARS != '', 'G_VARS must contain a |-separated list of variable names.';
	
	$varCount = GetToken($G_VARS, 'Count', '|');
	Assert $varCount > 0, 'G_VARS must contain a |-separated list of variable names.';

	$flagValue = 0;

	$localVars = <<<LOCALVARS
$bitValue
$flagValue
$G_FLAGS
$G_VARS
$i
$localVars
$varCount
$varName
LOCALVARS;

	// For each variables in G_VARS.
	$i = 1;
	while ($i <= $varCount) {
		// Get everything that depends on $i done with early.
		$bitValue = 2^($i-1);
		$varName = GetToken($G_VARS, $i, '|', 't');
		$i++;

		// Empty names are assumed to be false.
		if ($varName == '') { continue; }
		
		// Smartness to add '$' if missing.
		if ($varName UnLikeI '$*') { $varName = '$' . $varName; }

		// 
		Assert GetTokenIndex($varName, $localVars, "<crlf>", '') == 0, "Sorry, variable $varName is used locally and cannot be used in G_VARS.";

		// NOTE: Non-global variables are assumed false.
		Global *$varName;

		// Add the bit if it is true.
		if (*$varName) {
			$flagValue = $flagValue + $bitValue;
		}
	}
	Unset $varCount, $localVars, $i, $bitValue, $varName;

	Global $G_FLAGS = $flagValue;
	Unset $flagValue;
/*************************************************************** END _combine */


/*******************************************************************************
** Extract Flags
**   Sets global flag variables based on an OR-ed bitwise value.
*******************************************************************************/
"Extract Flags||4 : _extract"
	// -------------------- PARAMETERS -------------------------------------------
	// IN: G_FLAGS
	//  An OR-ed value to extract to the variables specified in G_VARS.
	Global $G_FLAGS;

	// IN: G_VARS
	//  A |-separated list of variable names to set.
	//  - Each variable will be made global and set to true (1) or false (0).
	//  - The list starts with bit-value 1, then 2, 4, 8, 16, etc.
	//  - Omitting variable names (not positions) will skip those bits.
	//  - The leading '$' is optional.
	//  - Variable names are trimmed of spaces before use.
	//  Examples:
	//      // Retrieves flags one, two, and four.
	//      $G_VARS = '$flagOne|$flagTwo|$flagFour';
	//      // Retrieves flags two (checkboxes) and eight (genericIcons) only.
	//      $G_VARS = '|checkboxes||genericIcons';
	Global $G_VARS;
	// -----------------END PARAMETERS -------------------------------------------

	// Parameter validation.
	Assert RegexReplace($G_FLAGS, '^[0-9]+$') == '', 'G_FLAGS must be a non-negative integer.';
	Assert $G_VARS != '', 'G_VARS must contain a |-separated list of variable names.';
	
	$varCount = GetToken($G_VARS, 'Count', '|');
	Assert $varCount > 0, 'G_VARS must contain a |-separated list of variable names.';

	// Leave the global value untouched.
	$flagValue = $G_FLAGS;

	// Determine how many bits are needed.
	$bits = 1;
	while (true) {
		$max = (2^$bits)-1;
		if ($max >= $flagValue) {
			break;
		}
		$bits++;
	}
	Unset $max;

	// Set unneeded variable names to false.
	while ($varCount > $bits) {
		$varName = GetToken($G_VARS, $varCount, '|', 't');
		$varCount--;

		// Skip setting empty variables.
		// This allows a G_VARS like 'one||four'
		if ($varName == '') { continue; }

		// Smartness to add '$' if missing.
		if ($varName UnLikeI '$*') { $varName = '$' . $varName; }

		Global *$varName = false;
	}
	Unset $varCount, $varName;


	// For each bit - in reverse.
	while ($bits >= 1) {
		$bitValue = 2^($bits-1);
		$varName = GetToken($G_VARS, $bits, '|', 't');
		// Get this done with so we can 'continue' later.
		$bits--;

		// Check if bit is set and if so remove it from flags.
		$isSet = $flagValue >= $bitValue;
		if ($isSet) {
			$flagValue = $flagValue - $bitValue;
		}

		// Skip setting empty variables.
		// This allows a G_VARS like 'one||four'
		if ($varName == '') { continue; }

		// Smartness to add '$' if missing.
		if ($varName UnLikeI '$*') { $varName = '$' . $varName; }

		Global *$varName = $isSet;
	}
	Unset $flagValue, $bits, $varName, $bitValue, $isSet;
/*************************************************************** END _extract */


/*******************************************************************************
** Expand Flags
**   Expands an OR-ed bitwise value into a set of flag variables.
**
**   This will determine how many bits are needed for the specified value and
**   save this to the global variable G_BIT_COUNT.
**
**   Then for each bit it will save whether or not it is set to a global
**   variable G_BIT_# where # is the position of the bit.
**
**   Example output for G_FLAGS = 1+4
**       G_BIT_COUNT = 3
**       G_BIT_1 = 1    // Flag 1
**       G_BIT_2 = 0    // Flag 2
**       G_BIT_3 = 1    // Flag 4
*******************************************************************************/
"Expand Flags||4 : _expand"
	// -------------------- PARAMETERS -------------------------------------------
	// IN: G_FLAGS
	//  An OR-ed value to extract to G_BIT_# variables.
	Global $G_FLAGS;
	// -----------------END PARAMETERS -------------------------------------------

	// Parameter validation.
	Assert RegexReplace($G_FLAGS, '^[0-9]+$') == '', 'G_FLAGS must be a non-negative integer.';

	// Leave the global value untouched.
	$flagValue = $G_FLAGS;

	// Determine how many bits are needed.
	$bits = 1;
	while (true) {
		$max = (2^$bits)-1;
		if ($max >= $flagValue) {
			break;
		}
		$bits++;
	}
	Unset $max;

	// Share count as G_BIT_COUNT.
	$varName = '$G_BIT_COUNT';
	Global *$varName = $bits;

	// Share the value of each bit as $G_bit#
	while ($bits >= 1) {
		$varName = '$G_BIT_' . $bits;

		$bitValue = 2^($bits-1);

		Global *$varName = $flagValue >= $bitValue;
		if (*$varName) {
			$flagValue = $flagValue - $bitValue;
		}

		$bits--;
	}
	Unset $flagValue, $bits, $varName, $bitValue;
/**************************************************************** END _expand */
[/size]

nerdweed
Posts: 648
Joined: 25 Feb 2012 07:47

Re: [Script] Bit Flag Helper

Post by nerdweed »

This is a gem. :appl:
Can it be converted a user function

TheQwerty
Posts: 4373
Joined: 03 Aug 2007 22:30

Re: [Script] Bit Flag Helper

Post by TheQwerty »

nerdweed wrote:This is a gem. :appl:
Can it be converted a user function
Thanks!

One day if I can find the time. ;)

highend
Posts: 13260
Joined: 06 Feb 2011 00:33

Re: [Script] Bit Flag Helper

Post by highend »

The one I'm using from this module:

Code: Select all

/* *****************************************************************
 * Check if bit <x> is set (true / false) in a value

 * Example(s):
 *   text isBitSet(33847, 16);
 *   Output: 1

 * All credits go to TheQwerty for creating the initial function
 * Bit = Value
 *  1 =    1,  2 =    2,  3 =     4,  4 =     8,  5 =    16,  6 =     32
 *  7 =   64,  8 =  128,  9 =   256, 10 =   512, 11 =  1024, 12 =   2048
 * 13 = 4096, 14 = 8192, 15 = 16384, 16 = 32768, 17 = 65536, 18 = 131072
 * etc.
***************************************************************** */

// https://www.xyplorer.com/xyfc/viewtopic.php?t=12999
// Turned the "_expand" part into a (slightly different) function
function isBitSet($value, $bit) {
    // Input validations
    end (regexmatches($value, "[^0-9+-]")), '$value' . " can only contain " . quote("0-9, + or -") . "characters , aborted!";

    $value = eval($value);
    end (regexmatches($value, "^-")), '$value' . " must be a non-negative integer, aborted!";
    end (!regexmatches($bit, "^[1-9]([0-9]+)?$")), '$bit' . " must be a non-negative integer, aborted!";

    // How many bits does $value have?
    $bits = 1;
    while (true) {
        $max = (2 ^ $bits) - 1;
        if ($max >= $value) { break; }
        $bits++;
    }

    // Run over all bits
    while ($bits >= 1) {
        $bitValue = 2 ^ ($bits - 1);

        $result = $value >= $bitValue;
        if ($result) {
            $value -= $bitValue;
        }

        // Return if $bit is reached
        if ($bits == $bit) {
            return $result;
        }
        $bits--;
    }

    // $bit wasn't found
    return false;
}
One of my scripts helped you out? Please donate via Paypal or paypal_donate (at) stdmail (dot) de

Post Reply