Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C & PHP: Storing settings in an integer using bitwise operators?

I'm not familiar with bitwise operators, but I have seem them used to store simple settings before.

I need to pass several on/off options to a function, and I'd like to use a single integer for this. How can I go about setting and reading these options?

like image 311
John Coates Avatar asked May 05 '10 14:05

John Coates


3 Answers

You sure can do it in PHP.

Let's say you have four booleans you want to store in a single value. That means we need four bits of storage space

0000

Each bit, when set individually, has a unique representation in decimal

0001 = 1 // or 2^0
0010 = 2 // or 2^1
0100 = 4 // or 2^2
1000 = 8 // or 2^3

A common way to implement this is with bit masks to represent each option. PHP's error levels are done this way, for example.

define( 'OPT_1', 1 );
define( 'OPT_2', 2 );
define( 'OPT_3', 4 );
define( 'OPT_4', 8 );

Then when you have an integer that represents 0 or more of these flags, you check with with the bitwise and operator which is &

$options = bindec( '0101' );
// can also be set like this
// $options = OPT_1 | OPT_3;

if ( $options & OPT_3 )
{
  // option 3 is enabled
}

This operator works as such: only bits that are set in both operands are set in the result

0101 // our options
0100 // the value of OPT_3
----
0100 // The decimal integer 4 evaluates as "true" in an expression

If we checked it against OPT_2, then the result would look like this

0101 // our options
0010 // the value of OPT_2
----
0000 // The decimal integer 0 evaluates as "false" in an expression
like image 104
Peter Bailey Avatar answered Sep 30 '22 01:09

Peter Bailey


It works pretty much the same way in both languages, a side by side comparison:

C:

#include <stdio.h>
#include <stdint.h>

#define FLAG_ONE 0x0001
#define FLAG_TWO 0x0002
#define FLAG_THREE 0x0004
#define FLAG_FOUR 0x0008
#define FLAG_ALL (FLAG_ONE|FLAG_TWO|FLAG_THREE|FLAG_FOUR)

void make_waffles(void)
{
   printf("Yummy! We Love Waffles!!!\n");
}

void do_something(uint32_t flags)
{
    if (flags & FLAG_TWO)
         make_waffles();
}

int main(void)
{
    uint32_t flags;

    flags |= FLAG_ALL;

    /* Lets make some waffles! */
    do_something(flags);

    return 0;
}

PHP:

<?php

define("FLAG_ONE", 0x0001);
define("FLAG_TWO", 0x0002);
define("FLAG_THREE", 0x0004);
define("FLAG_FOUR", 0x0008);
define("FLAG_ALL", FLAG_ONE|FLAG_TWO|FLAG_THREE|FLAG_FOUR);

function make_waffles()
{
    echo 'Yummy! We Love Waffles!!!';
}

function do_something($flags)
{
    if ($flags & FLAG_TWO)
       make_waffles();
}

$flags |= FLAG_TWO;
do_something($flags);

?>

Note, you don't absolutely need to use constants, I just use them out of habit. Both examples will run, I compiled the C version via gcc -Wall flags.c -o flags. Change flags in either example to anything but FLAG_TWO or FLAG_ALL and (sadly) no waffles will be made.

In the C version, you don't have to tickle the preprocessor, it could quite easily be an enum, etc - that's an exercise for the reader.

like image 43
Tim Post Avatar answered Sep 30 '22 00:09

Tim Post


quote "the idea is not good, really. you would better pass few boolean. if you want use bitwise then

function someFunc($options)
{

   if ($options & 1 != 0)
      //then option 1 enabled
   if ($options & (1 << 1) != 0)
      //then option 2 enabled      
   if ($options & (1 << 2) != 0)
      //then option 3 enabled      
}

"

What you have done would be okay if you were checking for a single value, although not optimal, so checking that a bit is enabled, but lets say we wanted to be able to match any, or exact we could have the following methods

function matchExact($in, $match) { // meets your criterion, as would a switch, case, but ultimately not suited for use with flags
    return $in === $match;
}

function matchAny($in, $match) { // meets original criterion with more lexical name however it returns true if any of the flags are true
    return $in |= $match;
}

if you then wanted to expand upon this by having specific actions only happening if bit x,y,z was enabled then you could use the following

function matchHas($in, $match) { // more bitwise than === as allows you to conditionally branch upon specific bits being set
    return $in &= $match;
}

I also think if you are doing what was done in the above quote, flags may not be the best idea, exact values might be better, which does have the benefit of allowing more discreet actions. (0-255) for 8-bit over 8 distinct flags

The whole reason flags work so well is because in base 2 "8" does not contain "4", and "2" does not contain "1".

 ________________________
 |8|4|2|1|Base 10 Value |
 ------------------------
 |1|1|1|1|15            |
 |1|1|1|0|14            |
 |1|1|0|1|13            |
 |1|1|0|0|12            |
 |1|0|1|1|11            |
 |1|0|1|0|10            |
 |1|0|0|1|9             |
 |1|0|0|0|8             |
 |0|1|1|1|7             |
 |0|1|1|0|6             |
 |0|1|0|1|5             |
 |0|1|0|0|4             |
 |0|0|1|1|3             |
 |0|0|1|0|2             |
 |0|0|0|1|1             |
 |0|0|0|0|0             |
 ------------------------
like image 21
Andrey Avatar answered Sep 30 '22 02:09

Andrey