3

Due to the restrictions of FMA Feature Parameters (see also How to scale FMA Feature management toggles) I want to use Apex 32Bit Integer to store 31 Feature toggles.

  • Only use positive Integers to make it easier (therefore 31 of 32 bits)
  • Having Feature 1 and 3 on should be internally this Bitstring 101
  • Having Feature 3 and 9 on should be internally this Bitstring 100000100

I know there are some Bit Operations (as described here in Apex but I didn't make it work.

public Integer fmaValueWithEnabled(List<Integer> featureIndexes) {
    Integer result = 0;

    for(Integer index : featureIndexes) {
        result = result | bitmask(index);
    }

    return result;
}

public Boolean isEnabled(Integer fmaValue, Integer featureIndex) {
    // There is no Feature 0, therefore -1
    Integer bitmask = bitmask(featureIndex - 1);
    return (bitmask  == (fmaValue & bitmask));
}

public Integer bitmask(Integer featureIndex) {
    Double result =  Math.pow(Double.valueOf(2), Double.valueOf(featureIndex - 1));
    return Integer.valueOf(result);
}
1
  • 1
    By the way, if you treat the bits as bits instead of numbers, you can use all 32 bits freely.
    – sfdcfox
    Oct 8 at 19:26
4

You've got your boolean operations incorrect. You only need OR (|), AND (&), BINARY NOT (~), XOR (^), and BITSHIFT (<<) to make this happen. Here's a example class for you.

public class Features {
    public enum FEATURE { OPTION1, OPTION2, OPTION3 }
    public static Integer flags = FeatureManagement.checkPackageIntegerValue('Flags__c');
    public static Boolean isFeatureEnabled(FEATURE feature) {
        return 0 != (flags & 1 << feature.ordinal());
    }
    public static void enableFeature(FEATURE feature) {
        flags |= 1 << feature.ordinal();
    }
    public static void disableFeature(FEATURE feature) {
        flags &= ~(1 << feature.ordinal());
    }
    public static void toggleFeature(FEATURE feature) {
        flags ^= 1 << feature.ordinal();
    }
    public static void saveValues() {
        FeatureManagement.setPackageIntegerValue('Flags__c', flags);
    }
}

Do not use non-bit-operator logic (e.g. any kind of math operator) on flags without carefully walking through how they might work.

Note that I'm using ordinal() with enum, but this design means you can't change the order of the flags easily. You might want to use an enum with a Map to explicitly track the flags in any order you desire:

Map<FEATURE, Integer> bitFlagIndex = new Map<FEATURE, Integer> {
  FEATURE.OPTION1 => 0,
  FEATURE.OPTION2 => 1,
  FEATURE.OPTION3 => 2
}
1

I think the issue here boils down to inconsistent handling of arguments.

In bitmask(), you're subtracting 1 from the passed featureIndex. You're also subtracting 1 from featureIndex in isEnabled() (when you call bitmask()).

So when isEnabled() is called with a featureIndex of 3, things proceed as follows

  • isEnabled(5, 3)
  • bitmask(2)
  • Math.pow(2, 2-1)
  • bitmask returns ...0010
  • ...0101 & ...0010 => ...0000
  • ...0010 != ...0000

A single line change to fix this would be

public Boolean isEnabled(Integer fmaValue, Integer featureIndex) {
    // Don't subtract from featureIndex (that's already being done in bitmask() )
    Integer bitmask = bitmask(featureIndex);
    return (bitmask  == (fmaValue & bitmask));
}

If you want/need to guard against feature 0 (or negative feature numbers), then I'd suggest some simple 'fail early' checks

public Integer fmaValueWithEnabled(List<Integer> featureIndexes) {
    Integer result = 0;

    for(Integer index : featureIndexes) {
        if(index <= 0){ continue; }
        result = result | bitmask(index);
    }

    return result;
}

public Boolean isEnabled(Integer fmaValue, Integer featureIndex) {
    if(featureIndex <= 0){ return false; }
    Integer bitmask = bitmask(featureIndex);
    return (bitmask  == (fmaValue & bitmask));
}

public Integer bitmask(Integer featureIndex) {
    Double result =  Math.pow(Double.valueOf(2), Double.valueOf(featureIndex - 1));
    return Integer.valueOf(result);
}

Manually tested the following cases, which seem to return the correct result (0101 = 5, 100000100 = 260):

  • isEnabled(5, 1) (true)
  • isEnabled(5, 3) (true)
  • isEnabled(5, 9) (false)
  • isEnabled(260, 1) (false)
  • isEnabled(260, 3) (true)
  • isEnabled(260, 9) (true)
  • isEnabled(260, 8) (false)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.