64-bit NS_OPTIONS bitmask

别等时光非礼了梦想. 提交于 2019-12-10 17:25:17

问题


Context

I'm using the NS_OPTIONS macro to create a bitmask. I've assigned it a type of NSInteger and since I'm building on a 64-bit platform, that should give me a total of 63 "slots" to work with (64 bits less one bit for signed-ness).

Here is the enumeration:

typedef NS_OPTIONS(NSInteger, LPSVGOPlugin) {
    LPSVGOPluginNone                            = 0,
    LPSVGOPluginCleanupAttrs                    = 1 << 0,
    LPSVGOPluginRemoveDoctype                   = 1 << 1,
    LPSVGOPluginRemoveXMLProcInst               = 1 << 2,
    LPSVGOPluginRemoveComments                  = 1 << 3,
    LPSVGOPluginRemoveMetadata                  = 1 << 4,
    LPSVGOPluginRemoveTitle                     = 1 << 5,
    LPSVGOPluginRemoveDesc                      = 1 << 6,
    LPSVGOPluginRemoveUselessDefs               = 1 << 7,
    LPSVGOPluginRemoveEditorsNSData             = 1 << 8,
    LPSVGOPluginRemoveEmptyAttrs                = 1 << 9,
    LPSVGOPluginRemoveHiddenElems               = 1 << 10,
    LPSVGOPluginRemoveEmptyText                 = 1 << 11,
    LPSVGOPluginRemoveEmptyContainers           = 1 << 12,
    LPSVGOPluginRemoveViewBox                   = 1 << 13,
    LPSVGOPluginCleanupEnableBackground         = 1 << 14,
    LPSVGOPluginMinifyStyles                    = 1 << 15,
    LPSVGOPluginConvertStyleToAttrs             = 1 << 16,
    LPSVGOPluginConvertColors                   = 1 << 17,
    LPSVGOPluginConvertPathData                 = 1 << 18,
    LPSVGOPluginConvertTransform                = 1 << 19,
    LPSVGOPluginRemoveUnknownsAndDefaults       = 1 << 20,
    LPSVGOPluginRemoveNonInheritableGroupAttrs  = 1 << 21,
    LPSVGOPluginRemoveUselessStrokeAndFill      = 1 << 22,
    LPSVGOPluginRemoveUnusedNS                  = 1 << 23,
    LPSVGOPluginCleanupIDs                      = 1 << 24,
    LPSVGOPluginCleanupNumericValues            = 1 << 25,
    LPSVGOPluginMoveElemsAttrsToGroup           = 1 << 26,
    LPSVGOPluginMoveGroupAttrsToElems           = 1 << 27,
    LPSVGOPluginCollapseGroups                  = 1 << 28,
    LPSVGOPluginRemoveRasterImages              = 1 << 29,
    LPSVGOPluginMergePaths                      = 1 << 30,
    LPSVGOPluginConvertShapeToPath              = 1 << 31,
    LPSVGOPluginSortAttrs                       = 1 << 32,
    LPSVGOPluginTransformsWithOnePath           = 1 << 33,
    LPSVGOPluginRemoveDimensions                = 1 << 34,
    LPSVGOPluginRemoveAttrs                     = 1 << 35,
    LPSVGOPluginAddClassesToSVGElement          = 1 << 36,
    LPSVGOPluginRemoveStyleElement              = 1 << 37
};

The top value is left-shifted 37 bits, which is well below the 63 bits I should have available.


The Problem

I create a mask from that enumeration, like this:

LPSVGOPlugin defaultPluginMask = LPSVGOPluginNone;
        defaultPluginMask = (LPSVGOPluginCleanupAttrs
                               |LPSVGOPluginRemoveDoctype
                               |LPSVGOPluginRemoveXMLProcInst
                               |LPSVGOPluginRemoveComments
                               |LPSVGOPluginRemoveMetadata
                               |LPSVGOPluginRemoveDesc
                               |LPSVGOPluginRemoveUselessDefs
                               |LPSVGOPluginRemoveEditorsNSData
                               |LPSVGOPluginRemoveEmptyAttrs
                               |LPSVGOPluginRemoveHiddenElems
                               |LPSVGOPluginRemoveEmptyText
                               |LPSVGOPluginRemoveEmptyContainers
                               |LPSVGOPluginCleanupEnableBackground
                               |LPSVGOPluginMinifyStyles
                               |LPSVGOPluginConvertStyleToAttrs
                               |LPSVGOPluginConvertColors
                               |LPSVGOPluginConvertPathData
                               |LPSVGOPluginConvertTransform
                               |LPSVGOPluginRemoveUnknownsAndDefaults
                               |LPSVGOPluginRemoveNonInheritableGroupAttrs
                               |LPSVGOPluginRemoveUselessStrokeAndFill
                               |LPSVGOPluginRemoveUnusedNS
                               |LPSVGOPluginCleanupIDs
                               |LPSVGOPluginCleanupNumericValues
                               |LPSVGOPluginMoveElemsAttrsToGroup
                               |LPSVGOPluginMoveGroupAttrsToElems
                               |LPSVGOPluginCollapseGroups
                               |LPSVGOPluginMergePaths
                               |LPSVGOPluginConvertShapeToPath
                               );

When I log the value of defaultPluginMask using this:

NSLog(@"maskValue: %li", (long)defaultPluginMask);

The result is -536879137. As soon as I remove that last mask addition (LPSVGOPluginConvertShapeToPath) from the mask, I get a positive value: 1610604511

Critically, LPSVGOPluginConvertShapeToPath is left-shifted 31 bits, which would be the maximum bit position in a 32-bit NSInteger. But if I use sizeof() to log the size of defaultPluginMask, it reports it as 8 bytes, which means it should be 64 bits.

I'm a bit (ha!) confused. Have I made a basic error I'm not seeing?


回答1:


For long data type, C standard specifies that the minimum range is required to be from -2147483648 to 2147483647, i.e. using 32 bits, including the sign bit.

Even if compiling for a 64 bits platform, it is not guaranteed that a long variable will be represented on 64 bits. To ensure that it will have minimum 64 bits of storage, long long should be used. As @Olaf recommended, to precisely specify the storage size, stdint.h types could be used (e.g. int64_t).

But the observations above doesn't seem to be the source of the issue in your case (if NSInteger and long sizes are >= 8 bytes).

The issue is that the value to be shifted 1 is an integer literal that fits in 32 bits and will be stored as such (if it wouldn't fit in 32 bits, e.g. 5 billions, it would be stored on 64 bits). And 1 << 31 will be a signed 32 bit integer, with 1 exactly on the sign position, so a negative number. And if this is assigned later to a 64 bit signed integer variable, the sign bit will be extended (two's complement representation).

This can be seen from the values observed in your case, which are represented in hexadecimal on 64 bits as:

 -536879137  =>  ffffffffdfffdfdf   - when 1 << 31 is present
 1610604511  =>          5fffdfdf   - when 1 << 31 is removed

Below I've done a small experiment to see what happens near this "border" (it should be noted that values obtained from 1 << 32 and 1 << 33 are undefined behaviour - shifting over the storage size, which on other platforms may happen to give the same results as 1 << 0 and 1 << 1 ).

printf("%d %d %d %d\n", sizeof(int), sizeof(long), sizeof(long long), sizeof(void*)); 
// Prints: 4 4 8 8, running on an Windows 64-bit, compiled with gcc from MinGW
long long a[9] = {
    1   << 30, //  1073741824         40000000
    1LL << 30, //  1073741824         40000000
    1   << 31, // -2147483648 ffffffff80000000
    1LL << 31, //  2147483648         80000000
    1   << 32, //           0                0
    1LL << 32, //  4294967296        100000000
    1   << 33, //           0                0
    1LL << 33, //  8589934592        200000000
    5000000000,//  5000000000        12a05f200
};
// Prints of this loop are in the comments above
for (int i = 0; i < 9; i++)
    printf("%12lli %16llx\n", a[i], a[i]);

Solution: If an integer literal is intended to be directly used in operations whose results won't fit in the storage size of that literal, it have to be suffixed with:

  • u/U for unsigned (if needed)

  • l/L for long

  • ll/LL for long long

The issue in the question may be solved by applying the LL suffix to these elements:

LPSVGOPluginConvertShapeToPath              = 1LL << 31,
LPSVGOPluginSortAttrs                       = 1LL << 32,
LPSVGOPluginTransformsWithOnePath           = 1LL << 33,
LPSVGOPluginRemoveDimensions                = 1LL << 34,
LPSVGOPluginRemoveAttrs                     = 1LL << 35,
LPSVGOPluginAddClassesToSVGElement          = 1LL << 36,
LPSVGOPluginRemoveStyleElement              = 1LL << 37


来源:https://stackoverflow.com/questions/33904321/64-bit-ns-options-bitmask

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!