I checked this question and answers and came up with the following idea.
int i = n-1; uint32_t y = x; while(y && i--) { y = y & (y << 1); };
After the operation is completed, y nonzero if there are n consecutive bits. The next thing to do is find the least significant value set there. The following code will separate all set bits except the least significant.
z = y - (y & (y-1));
Now that we have only one bit, we need to find the position of the bit. We can use the switch statement with 32 cases.
static inline int get_set_position(const uint32_t z) { switch(z) { case 0x1: return 0; case 0x2: return 1; .... ....
Finally, to get the result, we need to reduce n-1 . Thus, the general procedure is as follows.
static inline int get_set_n_position(const uint32_t x, const uint8_t n) { if(!n) return -1; int i = n-1; uint32_t y = x; while(y && i--) { y = y & (y << 1); }; if(!y) return -1; uint32_t z = y - (y & (y-1)); if(!z) return -1; int pos = get_set_position(z); if(pos < 0) return -1; assert(pos >= (n-1)); return pos - (n-1); }
Now there is concern about the big endian. I think I just need to change get_set_position () for big-endian to make it work (assuming that changes to the definition of consecutive bit sets are based on endian-ness).
Let me share the verified code that uses builtin_ctzl provided by gcc.
OPP_INLINE int get_set_n_position(BITSTRING_TYPE x, const uint8_t n) { if(!n || n > BIT_PER_STRING) return -1; int i = n-1; while(x && i--) { x = x & (x << 1); }; if(!x) return -1; int pos = __builtin_ctzl(x); return pos - (n-1); }
The code runs O (1) times because 32 is constant (as @Qnan noted). Again, it works in O (n) if the register size changes.
Note. I fixed bugs thanks to comments and unit testing.
source share