Page 1 of 1

Saving Settings using OR

#1 jhouns  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 25
  • View blog
  • Posts: 100
  • Joined: 21-December 09

Posted 27 November 2013 - 08:55 AM

Requirements
This tutorial assumes a basic knowledge of C# and basic binary/logical operators.

Scenario

Quote

You're writing an application with multiple settings that require saving to disk or to memory, but there are potentially hundreds of flags that need to be tracked/saved; the settings file doesn't necessarily have to be human readable and you want to save memory wherever you can.


The madness of bool
In order to appease the previous scenario, assume you have the following class with several arbitrary settings that need to be saved:
public class Example
{
    public Boolean usesFastBrowse { get; set; }
    public Boolean Validates { get; set; }
    public Boolean isHD { get; set; }
}



This seems like a perfectly valid series of properties to hold our flags don't you think? Each boolean represents something in the program and can easily be used like so:
if(exampleInstance.usesFastBrowse)
{
    // Code
}



But what you might not have considered, is how terribly inefficient this is. According to MSDN, a Boolean (or bool) is 8 bits in size, making our class above use 24 bits to store 3 flags.

Let me make that clear for you. 24 bits = 3 bytes. We are using 3 bytes to store 3 true/false pieces of information. This seems a tad excessive, considering the smallest value that can represent true/false is a single bit. Why is a bool a byte then? Not a clue. How does it work? what does it do with all those bits? well...
int y = 0;
int z = 1;
byte w = 255;

Convert.ToBoolean(y); // -> '0000 0000' - Zero? FALSE
Convert.ToBoolean(z); // -> '0000 0001' - Greater than 0. TRUE
Convert.ToBoolean(w); // -> '1111 1111' - Greater than 0. TRUE

So as a range:
0000 0001 -> 1111 1111 = TRUE
0000 0000 -> 0000 0000 = FALSE


The example above shows us how truly mad booleans are in C#, they use 255/256 possible combinations to mean the same thing when it could be eloquently summed up using a single bit and a trivial function to convert integer to bool.
1 = true
0 = false

Boolean intToBool(int i)
{
   return (i > 0);
}




So how can I break free of this madness when C# only offers datatypes of 8 bits or more? Oddly enough, you can look at another Microsoft work to determine a brilliant answer: the Windows API (or WinApi).

The WinApi
While I was playing around with the WinApi, I saw that a lot of settings were set using a similar pattern:
// Using the animate window function, don't worry too much about this, it's an example.
private static extern IntPtr AnimateWindow(IntPtr hwnd, int dwTime, int dwFlags);

// Usage:
// AnimateWindow(WINDOW_HANDLE, ANIMATION_LENGTH, ANIMATION_TYPE);
// ANIMATION_TYPES
 public enum TransitionType
{
    ACTIVATE      = 0x00020000, // Window activate/show
    BLEND         = 0x00080000, // Fade effect
    CENTER        = 0x00000010, // Zoom to square effect
    HIDE          = 0x00010000, // Hide the window
    LEFT_TO_RIGHT = 0x00000001,
    RIGHT_TO_LEFT = 0x00000002,
    SLIDE         = 0x00040000, // Slide effect
    TOP_TO_BOTTOM = 0x000000004,
    BOTTOM_TO_TOP = 0x000000008
}

// This made:
AnimateWindow(windowToAnimate.Handle,  // Window we want to animate
              2000, // How long the animation should take
              TransitionType.CENTER); // Animate using a 'zoom' style effect.


The above sample seemed brilliant! Just set the transition type and away it went. But what did those numbers actually mean? And what if I wanted to mix animations? Turns out it's VERY simple:
AnimateWindow(windowToAnimate.Handle,  // Window we want to animate
              2000, // How long the animation should take
              TransitionType.CENTER | TransitionType.ACTIVATE); // Animate using a 'zoom' style effect to make the window appear


There we go, we just told it to use the zoom function to show the window. What if we wanted to hide it? Just replace ACTIVATE with HIDE and we're good to go. Now this seems great, but what I still haven't said is what those numbers mean and how this relates to booleans! I didn't even say what that bar thing is! Well that's coming right up in...

Using OR to fix the madness
In the last example you saw me use the vertical bar (or pipe) to mix two settings, this is called the OR operator and its function is exactly the same as in electronics. It iterates through each bit in the two values and if one of them is a 1, the result is 1, otherwise, it's a 0 e.g.
0001
0011
-----
0011



Now in order to demonstrate how it applies to the enum we will use our original example only modified somwhat:
public class Example
{
    byte settings = 0;
}

public enum ExampleSettings
{
    FAST_BROWSE = 1,
    VALIDATE = 2,
    USE_HD = 4
}


Now we have completely done away with our booleans and replaced them with a singular byte. But we've also created an enum to hold a list of the possible flags we need to monitor. But WHY ARE THEY NUMBERED LIKE THAT!?!? It's simple really! Remember how we said we only really need 1 bit to determine if something is true or false? Well that means to show three flags we only need 3 bits!
//USE HD VALIDATE  FAST_BROWSE
    0       0          0     


By changing the '0's to '1's we can turn different features on or off and if we take away the headings we are left with:
000 // Nothing on
100 // Use HD
010 // Validate
001 // Fast browse 


Look familiar? They should do! They're binary numbers! Look again!
000 = 0 // Nothing on
100 = 4 // Use HD
010 = 2 // Validate
001 = 1 // Fast browse 


So that's how we give them their special numbers. We give them the integer representation of their on state in binary. Why would we do this? So we can manipulate our settings number with ease. Because C# doesn't let us use less than 1 byte our settings string is 8 empty bits structured like so:
// Unused   USE HD VALIDATE  FAST_BROWSE 
   0000 0      0       0          0       


Okay, so we can in theory represent 8 different flags in this one byte. That's 1/8th of the memory of using 8 booleans. Cool right? But don't PC's have a LOT of memory anyway? Well, yes. But what if you were performing millions of writes per second? or sending a lot of data across a network? A small size is a big benefit in these situations.

So now we're clear on WHY it's a good idea, how would you go about changing the individual bits? It's not like you can index into an int like an array. Fortunately here is where OR comes in. Let me show you an example
settings = 0000 0000; // Empty byte

settings = FAST_BROWSE; // 0000 0001
settings = FAST_BROWSE | VALIDATE; 
// 0000 0001 | 0000 0010
// AKA:
// 0000 0001 | 
// 0000 0010
// ------------
// 0000 0011

// settings = 0000 0011


If fast browse was 1 and validate was 10 ORing the 2 gives you 11, telling you that both are active. See how that works?
So to activate a setting we can use:
settings |= <SETTING_TO_ACTIVATE>


Now, that setting to activate has to be a number, but to make it more readable, we use an enum but you could easily use any old variable. That's all there is to it!

Now you know how that works, how can you do some other simple stuff? What about setting a flag to off? For this we use XOR!
settings = 0000 0011; // Fast_browse/Validate

settings ^= FAST_BROWSE; // Turn off Fast browse. (0000 0011 XOR 0000 0001)
//               XOR = ^
// 0000 0011 ^  (Settings)
// 0000 0001    (Fast Browse)
// ---------
// 0000 0010



And what about checking if a flag is set? We have another friend for that. He's called ANDy.
settings = 0000 0011; // Fast_browse/Validate

settings & FAST_BROWSE // 0000 0011 AND 0000 0001
// 0000 0011 &
// 0000 0001    (FAST_BROWSE)
// ----------
// 0000 0001

// Another example
settings & VALIDATE // 0000 0011 AND 0000 0010
// 0000 0011 &
// 0000 0010   (VALIDATE)
// ----------
// 0000 0010


Notice how the result matches what we're trying to match? If it doesn't it will return 0 instead, this means we can use it in if statements like so:
// Usage:
if(VALIDATE == (settings & VALIDATE))
{
  // Code
}



Now you may this this is rather silly, look at all that extra work for a bit of saved memory. This is where I concede, you shouldn't be using this all the time. It hurts code readability, sure you can encapsulate this logic in a function, but it's more long winded. If you're not trying to save memory/space then you should just use the standard boolean values. Yes, it takes up more room, but isn't
if(usesValidation)
{
}


a lot nicer to read and easier to understand than
// Usage:
if(VALIDATE == (settings & VALIDATE))
{ 
}


?

Is This A Good Question/Topic? 2
  • +

Replies To: Saving Settings using OR

#2 Ryano121  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1363
  • View blog
  • Posts: 3,002
  • Joined: 30-January 11

Posted 16 December 2013 - 02:44 PM

Just to clear up the smallest addressable size of a value is a byte which is why a bool is 8 bits (a byte) in C# (and C++ for that matter).
Was This Post Helpful? 1
  • +
  • -

#3 andrewsw  Icon User is offline

  • It's just been revoked!
  • member icon

Reputation: 3822
  • View blog
  • Posts: 13,544
  • Joined: 12-December 12

Posted 07 February 2014 - 01:06 PM

I'm confused, settings is a byte and we cannot just assign it to one of the enums (or an ORed version of them) as they are integers.

We can use the Flags() attribute:

    class Program {
        [Flags()]
        public enum ExampleSettings {
            FAST_BROWSE = 1,
            VALIDATE = 2,
            USE_HD = 4
        }
        static void Main(string[] args) {
            ExampleSettings settings;
            settings = ExampleSettings.FAST_BROWSE | ExampleSettings.VALIDATE;
            Console.WriteLine(sizeof(ExampleSettings));     // 4
            Console.WriteLine(sizeof(int));     // 4
            Console.ReadKey();
        }
    }

ExampleSettings is an integer and we can use the sequence 0,1,2,4,8,16 for the enum values.

Have I misunderstood?
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1